【问题标题】:How can I mock the presence of a properties file on the classpath?如何模拟类路径上属性文件的存在?
【发布时间】:2015-09-28 14:59:48
【问题描述】:

这肯定是一个普遍的问题。我有一个像my-settings.properties 这样的属性文件,由应用程序类读取。当我编写一个测试类时,它需要测试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 复制它,然后在测试后再次将其删除。但这很笨拙并且涉及大量冗余。更不用说我什至不确定类路径目录是否可写。
  2. 使用模拟框架模拟getResource。不知道我会怎么做,尤其是有上百万种不同的方式来获取文件(this.getClass().getResourceAsStream(...)MyClass.class.getResourceAsStream(...)ClassLoader.getSystemClassLoader().getResourceAsStream(...) 等)

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

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

【问题讨论】:

  • 这是一个非常好的问题。您是否频繁地获取Properties 对象以能够将其存根?即使有一百万种获取属性的方法,您的代码是否以统一的方式访问属性文件,这会消除您必须模拟的一些测试向量?或者它是一个黑盒子,你不知道?
  • 我的代码目前确实以统一的方式获取属性,但我不希望我的测试在未来因为有人更改了获取属性文件的方式而失败。不知道你所说的“存根”是什么意思。你的意思是模拟Properties 类的构造函数和load 方法什么都不做,然后模拟getProperty 方法只返回我在测试类中创建的映射的属性?这是个好主意……尽管验证我将正确的文件名传递给getResourceAsStream(等等)并没有帮助
  • 是的,这就是我的提议,你说得对,这不是面向未来的。我觉得验证正确的文件名应该是一个不同的测试......
  • @ShotgunNinja 是的,它应该是一个不同的测试,但是怎么做呢?如果我基本上忽略了对getResourceAsStream 等的调用,则通过模拟Properties 类本身。
  • @Cassian 我很难理解您的评论。 Java 属性只接受属性文件中的每个键的一个值,我的应用程序也是如此。我不认为我是在“测试是否从不同的文件中传递了一个变量”,尽管我也不太明白这意味着什么。

标签: java unit-testing junit properties mocking


【解决方案1】:

我发现在处理文件时,最好引入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

【讨论】:

  • 我的班级的消费者不需要知道属性文件的位置。当然,我可以创建一个空构造函数,它只是将 DEFAULT_RESOURCE 传递给单参数构造函数,但随后我的消费者调用了与我的测试类不同的方法。
  • 这就是我要做的,添加方法来简化测试是很常见的......我会让一个 arg 构造函数受保护而不是公开,因为它仅用于测试
  • 哦,你的意思是包保护?这可能是可用解决方案的最佳想法。我仍然感到很惊讶,但没有现成的解决方案来解决这个问题!
  • 不受包保护...只是受保护。依赖注入是最好的解决方案......其他任何事情都会为每个测试文件引入不同的类加载器,这非常混乱!!!
  • 不确定如果它只是受保护而不是受包保护,如何访问它。我的测试类没有从我的测试类扩展。因此,我不希望它能够访问受保护的(而不是受包保护的)类成员。
【解决方案2】:

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

public class MyService {

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

【讨论】:

  • 这基本上是@LanceJava 所建议的,只是使用资源而不是字符串...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-30
  • 2021-02-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多