【问题标题】:Different Singleton instances with JUnit tests带有 JUnit 测试的不同 Singleton 实例
【发布时间】:2010-01-30 07:40:55
【问题描述】:

我有一个成功通过测试的独立单例。但是对于一组测试,这会失败,因为一旦定义了单例,它就不允许重置实例。

关于如何解决这个问题的任何想法?

【问题讨论】:

    标签: junit singleton


    【解决方案1】:

    我假设你的单例类中有一个私有静态字段来存储初始化的实例。

    如果您不想修改代码,您可以定义一个在每次测试后运行的拆卸方法,并在此方法中通过反射将此静态字段设置为 null,如 here 所示。

    【讨论】:

    • +1 用于提供实际解决方案。我无法控制作为单例的 3rd 方代码,并且需要解决方案,但不建议应该如何完成。
    【解决方案2】:

    不要使用单例。

    具体来说,单例和全局变量之间的唯一区别是单例尝试强制执行单个实例(例如,通过将构造函数设为私有)。

    相反,将构造函数公开并使用新实例编写测试。在您的实际程序中,使用getInstance() 获取规范的全局实例(或使用 IOC 容器)。

    记住singletons are pathological liars

    如果您仍然对 Singleton 的想法感到满意,那么您可以添加一个公共(和静态)工厂方法来创建实例,而不是让构造函数公开,从而以一种不会被意外使用的方式创建实例,例如:

    public static MyClass TEST_CreateInstance() {
      return new MyClass();
    }
    

    【讨论】:

      【解决方案3】:

      Spring 为这个特定用例提供了DirtiesContext 注解,您需要为每个测试用例创建新的单例 bean 实例。它基本上为每个应用了此注释的测试用例/测试类创建一个新的应用程序上下文。

      【讨论】:

      • OP 没有说明他使用的是哪个工具,但事实证明我使用的是 spring,所以谢谢你的回答。您的建议适用于我 99% 的用例,但在一个特定情况下失败:我需要在测试中重置单例,该测试旨在运行工作负载两次,并在进行各种一致性检查后故意第二次失败。对此有什么想法吗?我正在挖掘“以编程方式重建整个弹簧上下文”的路线,但这并不让我高兴......而且我有目的地避免按顺序运行两个单独的测试,因为这使得测试不可读恕我直言......
      【解决方案4】:

      你可以添加一个方法来销毁单例,例如destroyMe();您在其中取消初始化所有内容并将单例的实例设置为 null。

        public void destroyMe(){
         this.instance = null;
         //-- other stuff to turn it off.
      }
      

      不过我会留下同步问题;)

      但是为什么每次测试都需要重新初始化单例呢?它不应该基于单例的概念而有所不同。

      【讨论】:

        【解决方案5】:

        强烈建议摆脱单例作为设计模式,并使用单例作为范围(依赖注入)。这只会让你的问题消失。

        但是假设你被困在单例的世界里,那么你有几个选择取决于你是在测试单例还是依赖项。

        如果您正在测试依赖项,那么您可以使用PowerMockJMockIt 模拟单例。有关如何进行此操作的说明,请参阅我的 previous post 关于模拟 Runtime.getRuntime。

        如果你在测试 Singleton 那么你需要放宽构造规则,或者给 Singleton 一个“Reset”方法。

        【讨论】:

          【解决方案6】:

          通常要小心单例,它们通常是邪恶的、糟糕的设计,并且往往代表大的令人讨厌的全局变量(这不利于维护)。

          仍然要先进行测试,您可以这样做:

          static setInstance(...){ //package visibility or in difficult cases you have to use public instance = ...; }

          正如所说,这更像是一种解决方法。因此,首先要进行测试,然后再从单例模式中重构出来。

          【讨论】:

            【解决方案7】:

            单例实例需要通过测试本身传递给 SUT - 这样您就可以为每个测试创建单例(并销毁)。采用 IoC 和模拟框架(如 Mockito)将使这种方法变得微不足道。

            【讨论】:

              【解决方案8】:

              在这里聚会已经很晚了,但是对于任何正在寻找答案的人,以防您不想/无法修改代码。

              @BeforeEach
              public void setup() {
                  
                  object = Singleton.getInstance();
              }
              
              @AfterEach
              public void after() {
                  // cleaning the singleton instance
                  ReflectionTestUtils.setField(object , "internal_object_name", null);
              }
              

              你的 Singleton 类应该是这样的:

              public final class Singleton {
              
              private static Singleton internal_object_name;
              
              private Singleton (){}
              public static Singleton getInstance() {
                  if (object == null)
                       return new Singleton();
                  else
                       return internal_object_name;
               }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2011-08-23
                • 2014-07-03
                • 1970-01-01
                • 2015-07-21
                • 1970-01-01
                • 1970-01-01
                • 2014-03-09
                相关资源
                最近更新 更多