如何使用jmockit对Singleton模式进行单元测试

时间:2015-06-13 03:08:09

标签: java unit-testing design-patterns singleton jmockit

有一个java类,它基于Singleton模式。如何为这门课进行单元测试?以下是我的相关代码:

public class ConfigFromFile implements ConfigStrategy {
  private String endpoint;
  private final static String CONFIG_FILE = "/conf/config.properties";

  private InputStream getInputStream(String configFilePath) throws FileNotFoundException{
    return new FileInputStream(configFilePath);
  }

  private void initFromFile() {
    Properties prop = new Properties();
    InputStream input = null;
    try {
      input = getInputStream(CONFIG_FILE);
      prop.load(input);
      endpoint = prop.getProperty("endpoint");
    } catch(Exception e) {
      e.printStackTrace();
    } finally {
      if (input != null) {
        try {
          input.close();
        } catch (IOException e) {
        }
      }
    }
  }

  private ConfigFromFile() {
    initFromFile();
  }

  private static class ConfigFromFileHolder {
    private static ConfigFromFile instance = new ConfigFromFile();
  }

  public static ConfigFromFile getInstance() {
    return ConfigFromFileHolder.instance;
  }

  @Override
  public String getEndpoint() {
      return endpoint;
  }
}

我需要为这门课写单元测试。

  • 单元测试无法调用外部资源,因此我们需要模拟调用“/conf/config.properties”文件。我们可以使用jmockit。
  • 此类基于Singleton模式。我们希望这两种情况之间的相互作用不会受到影响。

以下是我的情况:

  1. Case1,正常情况下,文件内容为“endpoint = www.baidu.com”
  2. Case2,这是一个异常情况,我们可以模拟这个文件不存在。
  3. 如何实施这些案例?谢谢!

1 个答案:

答案 0 :(得分:0)

您可以添加一个包范围构造函数,该构造函数接收要加载的文件的路径,并让您的测试在临时文件夹中创建一个假文件,然后创建一个测试实例,传递到文件的路径。没有必要嘲笑。

public class ConfigFromFile implements ConfigStrategy {
  private static final String CONFIG_FILE = "/conf/config.properties";
  private final String configFilePath;
  private String endpoint;

  // Visible for testing
  ConfigStrategy(String configFilePath) {
    this.configFilePath = configFilePath;
  }

  private ConfigStrategy() {
    this(CONFIG_FILE);
  }

  private void initFromFile() {
    try (InputStream input = new FileInputStream(configFilePath)) {
      Properties prop = new Properties();
      prop.load(input);
      endpoint = prop.getProperty("endpoint");
    } catch(Exception e) {
      e.printStackTrace();
    }
  }

  ...

}

注意我已将initFromFile()重写为使用try-with-resources ,如果您使用的是JDK 7或更高版本,则可以执行此操作。

您可能想重新考虑此类是否需要是静态单例。它不仅使代码难以测试,而且还可以配置为在不同环境中使用不同的文件。请记住,类的测试是该类的第一个API用户。如果您的测试难以编写,则表明您的API存在问题。

如果您需要限制为一个实例,我建议使用依赖注入框架,如Guice或Spring。如果你使用Spring,你的类可以实现InitializingBean并在构造类之后但在将它注入另一个类之前加载配置文件:

public class ConfigFromFile implements ConfigStrategy, InitializingBean {
  private final String configFilePath;
  private String endpoint;

  ConfigStrategy(String configFilePath) {
    this.configFilePath = configFilePath;
  }

  @Override
  public void afterPropertiesSet() throws IOException {
    try (InputStream input = new FileInputStream(configFilePath)) {
      Properties prop = new Properties();
      prop.load(input);
      endpoint = prop.getProperty("endpoint");
    }
  }

  @Override
  public String getEndpoint() {
    return endpoint;
  }
}

是的,这就是您需要的所有代码。

  • 无需捕捉异常
  • 加载配置文件的异常不会被忽略
  • 无需getEndpoint()检查初始化是否失败

你可以和Guice一起做something similar

但是等等,如果你使用Spring,并且你愿意将端点值移动到Spring配置中,那么它会变得更简单!

public class ConfigFromSpring implements ConfigStrategy {
  private final String endpoint;

  ConfigStrategy(String endpoint) {
    this.endpoint = endpoint;
  }

  @Override
  public String getEndpoint() {
    return endpoint;
  }
}

课程非常简单,不需要任何单元测试。

您的bean XML看起来像这样:

<bean id="config" class="examples.ConfigFromSpring">
  <constructor-arg type="String" value="end of the road"/>
</bean>

当然,此时我将摆脱ConfigStrategy并在使用配置的对象上设置配置。