【问题标题】:Test singleton with jUnit使用 jUnit 测试单例
【发布时间】:2012-05-09 06:34:34
【问题描述】:

我有一个工厂类来检索我的应用程序的配置:

public class ConfigurationFactory {
private static ConfigurationFactory configurationFactory = new ConfigurationFactory();

private Configuration configuration = null;

public static ConfigurationFactory getConfigurationFactory() {
        return configurationFactory;
    }

还有一些 getConfiguration 方法,具体取决于我从哪里获取配置(文件、数据库、默认值等...):

public Configuration getConfiguration(String path){...}
public Configuration getConfiguration(String database){...}

我的问题是,当我对每个方法进行单元测试时,我必须重新启动单例以便从不同的源加载,所以我唯一想到的就是添加这个:

public void resetConfiguration() {
 this.configuration = null;
}

而且我觉得我会因为这样做而在开发者的地狱中燃烧:)

我的问题:有没有其他方法可以在不添加此方法的情况下做到这一点?

注意:我见过this,我不能使用像 Spring 或 Guice 这样的任何 DI 框架,管理层认为添加框架会使项目变得重量级,因为该程序旨在作为服务器上的守护进程运行。

【问题讨论】:

  • 重置实例是测试单例时的一种方式,因为它们总是很难测试并且没有任何好的方法。有时这类问题意味着在你的设计中有一些可以重构的东西。你会考虑重构吗?
  • 是的,但是,我该如何重构它呢?我的意思是,我花了一些时间寻找最好的方法,而单例工厂似乎是一个很好的模式,它确实以一种清晰的方式完成了工作。我唯一的问题是测试,但我不想重构整个事情只是为了适应 jUnit 测试......
  • 我将单元测试视为代码质量计量器。如果我遇到无法测试的东西,我倾向于重构那段代码。但是,当然,这是你的代码,如果这个可测试性问题不困扰你,重置实例就可以了。例如,您是否看过抽象工厂设计模式?我认为这应该更可测试......

标签: java junit singleton


【解决方案1】:

我能想到的一些事情是

  1. 使用 Easymock 或 Mockito 等模拟框架来模拟 ConfigurationFactory 并在其他调用区域使用此模拟对象。

  2. 在ConfigurationFactory 类中定义一个setter,并定义一个像@TestPurpose 这样的注解,并使用它来覆盖单元测试中的单例对象。注解表示在应用程序流程中不应使用此功能,并且该功能仅用于junit目的。

【讨论】:

  • 一个简单的问题,你想为 ConfigurationFactory 或其他依赖于 ConfigurationFactory 的类编写单元测试。如果是其他课程,那么您绝对可以尝试模拟框架。
  • 不,只是为了测试ConfigurationFactory。例如,测试是从文件加载配置、从数据库加载等。
  • 啊..我明白了,那么模拟将有助于模拟配置。如果这个类的构造函数不是为了加载所有这些,那么你绝对可以为这个类编写单元测试。您可以为方法 getConfiguration 返回模拟对象并执行断言。
  • 看来机会是用DI或者这个,所以我接受它
【解决方案2】:

首先要解决的是你的单例不应该是静态的。这里有几篇文章向您展示如何做到这一点:

一旦你解决了这个问题,你将不再需要重置你的单例,因为你可以随意注入它们。

【讨论】:

    【解决方案3】:

    如果您不想使用建议的框架,您仍然可以拆分 'src' 和 'test-src' 并使用受保护的方法而不是公共方法。这并不理想,但可以防止调用该重置方法,同时使您能够进行测试。

    • src/main/myproject.myfactory.ConfigurationFactory
    • src/test/myproject.myfactory.TestConfigurationFactory

    -> 在您的 JUnit 类中使用 @After 或 @AfterClass -方法调用重置

    如果您在多个测试场景中都需要工厂,您还可以创建一个抽象的 BaseTest 类,其中包含一个 @AfterClass 方法来清理此类单例。

    您也可以通过反射在您的测试代码中强制重置,并且根本不使用额外的重置方法,但我不建议这样做。

    【讨论】:

      【解决方案4】:

      你可以使用反射,例如

      public void resetSingleton() throws Exception {
         Field instance = FormatterService.class.getDeclaredField("instance");
         instance.setAccessible(true);
         instance.set(null, null);
      }
      

      见an example

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-21
        相关资源
        最近更新 更多