我如何模拟类路径中属性文件的存在?

How can I mock the presence of a properties file on the classpath?

这肯定是一个普遍的问题。我有一个类似 my-settings.properties 的属性文件,它由应用程序 class 读取。当我编写测试 class 时,它需要测试可能出现在 my-settings.properties 中的不同场景,以确保最大的代码覆盖率(例如空属性文件、基本属性文件等)。但是我的src/test/resources.

只能有一个my-settings.properties

要是有注释就好了

@MockFileOnClassPath(use = "my-settings-basic.properties", insteadOf = "my-settings.properties")

然后我可以在我的 /src/test/resources 中有多个 my-settings-XXX.properties 文件,并在每个测试方法上注释正确的文件。但我找不到这样的东西。我正在使用 JUnit 4.12。

我能想到几个粗略的解决方案:

  1. 每次测试前,在文件系统中找到该文件,使用文件系统I/O复制它,然后在测试后再次删除它。但这是笨拙的并且涉及很多冗余。更不用说我什至不确定 classpath 目录是否可写。
  2. 使用模拟框架来模拟 getResource。不知道我该怎么做,尤其是因为有上百万种不同的方法来获取文件(this.getClass().getResourceAsStream(...)MyClass.class.getResourceAsStream(...)ClassLoader.getSystemClassLoader().getResourceAsStream(...) 等)

我只是认为这一定是一个常见问题,也许 JUnit、Mockito、PowerMock、EasyMock 或类似的东西中已经有解决方案?

EDIT:有人指出此问题与 Specifying a custom log4j.properties file for all of JUnit tests run from Eclipse 重复,但事实并非如此。这个问题是关于想要在主调用和测试调用之间有一个不同的属性文件。对我来说,我想在测试调用和另一个测试调用之间有一个不同的属性文件。

我发现在处理文件的时候,最好引入一个Resource的概念。

例如:

public interface Resource {
    String getName();
    InputStream getStream();
}

然后你可以通过依赖注入传递资源:

public class MyService {
    private final Properties properties;

    public class MyService(Resource propFile) {
        this.properties = new Properties();
        this.properties.load(propFile.getStream());
    }

    ...
}

然后,在您的生产代码中,您可以使用 ClasspathResourceFileResourceURLResource 等,但在您的测试中,您可以使用 StringResource 等。

请注意,如果您使用 spring,您已经实现了这个概念。更多详情 here

您可以更改 Service class 以接受 resource 文件的名称,然后使用 name 加载 resource

public class MyService {

 public MyService(String resourceFileName){
   //and load it into Properties  getResourceAsStream(resourceFileName);
 }
}