如何更改单元测试的"private static final String filePath"值



我有我的类

    public class MappingLoader
    {
     private static final String filepath = "/tmp/mapping.properties" // unix path of production system
     private static Map<String,String> mapping = new HashMap<String,String>()
     static
     {
        loadMappingFile()
     }

@VisibleForTesting     
static void loadMappingFile()
    {
      //reading properties files here
     final Properties prop = new Properties();
        try (final InputStream input = Files.newInputStream(Paths.get(filepath)))
        {
            prop.load(input);
        }
        catch (final Exception e)
        {
            ...
            ...
            throw new RuntimeException(e);
        }
               //now load "mapping" from properties file
              ....
                  ....
        }
    }

为了测试,我需要改变字符串变量"filepath"的值,以便它应该采用开发系统路径(例如c:projecttargetmapping.properties)

我在junits中尝试过powermock,但是它总是抛出异常并终止。

类级别的注释:

@RunWith(PowerMockRunner.class)
@SuppressStaticInitializationFor("some.package.ClassWithStaticInit")

在测试用例中:

Whitebox.setInternalState(Mappingloader.class, "filepath", testfilepath);
Mappingloader.loadMappingFile();

我也试图通过反射来改变这一点(使用Java反射改变私有静态最终字段),但它总是抛出FileNotFoundException为"filepath",不采取改变的路径"testFilePath"

是否有任何方法我可以改变这个变量,使它不抛出FileNotFoundException没有任何修改的源代码?

如果我在源代码中删除"throw new RuntimeException(e);", powermock将为我工作。但是我想在不修改源代码的情况下实现这一点,无论是通过powermock,反射api。

那么,您可以试试Powermock的运气;这应该是可行的(也许如果你花更多的时间阅读它的文档并进行实验);但老实说:你的问题不在于测试。您的问题是您创建了不可测试的代码。现在你正试图使用大的powermock锤子来"修复"你的破碎的设计

你看,当使用static方法和常量时;人们认为他们"节省"了性能(这是真的;但在一个非常小的度;这对99.999%的应用程序来说可能并不重要);但是他们总是忘记使用静态会导致直接耦合不同的功能。static在良好的OO设计中是异常的;并应谨慎使用。

因此,在您的情况下,您可以将整个内容替换为以下内容:

interface MappingLoader {
  Map<String, String> loadMappingsFrom(String fileName);
}
class MappingLoaderImpl implements MappingLoader { 
  ...

,你看,突然之间你只处理"真正的"接口和类;以及非静态方法;令人惊讶的是:现在你可以对整个程序进行单元测试了;最有可能的是,您甚至不需要一个mock框架。你只需要在某个地方创建一个带有映射的临时文件;然后你要确保你的impl类给你那个文件的映射。零嘲笑;内部实现细节的零测试;只有几个断言。

和另一个优势:所有应该只有使用MappingLoader 接口的客户端代码也可以被测试。因为像EasyMock或Mockito这样的普通框架将允许您模拟出该接口的实例……因为:不再有静态调用了!

这就是改变私有final静态字段值的方法——不使用它们!

(如果我让你感到好奇:看看这篇文章,学习如何从头开始编写可测试的代码)

虽然我完全同意@GhostCat的回应,但我明白你正在寻找一个不涉及更改源代码的解决方案。您是否想过在测试运行之前更改/tmp/mapping.properties的内容(并在之后恢复它们)?

static final String字段或任何final static原语字段不能在运行时修改。如果速度诚实,你可以修改这些字段,但你的改变不会影响代码使用这些字段,因为在编译期间引用被替换为值。

我的建议:使用static mock Files.newInputStream()调用,然后返回ByteArrayInputStream与预期的数据。在这种情况下,您将避免可能影响测试稳定性的脆弱磁盘IO操作。

最新更新