关于java:使用jUnit测试单例

Test singleton with jUnit

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

1
2
3
4
5
6
7
8
public class ConfigurationFactory {
private static ConfigurationFactory configurationFactory = new ConfigurationFactory();

private Configuration configuration = null;

public static ConfigurationFactory getConfigurationFactory() {
        return configurationFactory;
    }

以及一些getconfiguration方法,具体取决于从何处获取配置(文件、数据库、默认值等):

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

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

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

我觉得这样做会让开发者大吃一惊的:)

我的问题是:没有添加这个方法,还有其他方法可以做到吗?

注意:我已经看到了这一点,我不能使用任何像Spring或Guice这样的DI框架,管理层认为添加一个框架会使项目变得非常重要,因为这个程序将作为服务器上的守护进程运行。


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

  • 恢复单身模式。
  • 依赖注入。

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


我能想到的是

  • 使用类似于EasymockMockito的模拟框架来模拟ConfigurationFactory,并在其他调用区域使用这个模拟对象。

  • ConfigurationFactory类中定义一个setter,并定义一个类似@TestPurpose的注释,并使用它从单元测试中重写singleton对象。注释的目的是表示不应在应用程序流中使用该函数,并且该函数仅用于JUnit目的。


  • 例如,您可以使用反射

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

    看到一个例子


    如果您不想使用建议的框架,您仍然可以拆分"src"和"test src",使用受保护的方法而不是公共方法。这并不理想,但可以在允许测试的同时阻止对重置方法的调用。

    • src/main/myproject.myfactory.configurationfactory
    • src/test/myproject.myfactory.testconfigurationfactory

    ->在JUnit类中,使用@after或@after class-方法调用reset

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

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