我有一组JUnit测试,它们在Java程序上调用main方法,传入args并检查输出。没关系。
但是,如果我正在测试的程序具有被更改的静态值,则它们在测试之间将保持不变。这会导致问题。我无法准确控制正在测试的程序,或者用于静态字段的名称。
如何确保我的单元测试运行干净,就好像它是从头开始程序,而不保留那些静态字段。有没有办法以某种方式重置它们?
如果没有,我将不得不启动一个运行该程序的新进程,然后检查输出等,但这似乎有点矫枉过正。
编辑 - 请注意我无法控制单元测试正在测试的代码 - 我无法更改其字段名称,不幸的是,我也不会知道他们的字段名称。我认为如果没有开始新的流程,这是不可能的?
答案 0 :(得分:6)
您应该显式初始化测试类中的任何静态状态,通常这是在注释为@Before
或@BeforeClass
这是一个原因,其中包括为什么在应用程序中拥有大量静态依赖项是一个不好的测试方法。这就是为什么许多人鼓励无国籍编程。
答案 1 :(得分:4)
一般情况下,如果您发现代码不可测试,就像这里的问题一样,这是代码味道的标志,您应该认真考虑重构代码以避免使用这些静态字段。
话虽如此,您可能会发现BeanInject library很有帮助。您可以将@After
带注释的方法放入测试类中,并使用注入重置静态字段:
Inject.field("thatStaticField").of(thatObjectWithStaticFields).with("default value");
这样你只需要知道字段名称,但是你不必用字段来实际修改类。图书馆使用反射来做到这一点。
此外,我想到,如果你正在测试包含你无法控制的部分的东西,你为什么不尝试用Mockito来模仿这些部分?
编辑/添加:好的,所以你的问题是你甚至不知道类可能有也可能没有的可能静态变量的初始值。我看到了两种可能的方法:1)你必须在第一次加载类时保存它们的值并在每次测试之间重置值,或者2)你必须得到一个全新的类实例。类加载器。
在第1点),您需要使用反射循环遍历@BeforeClass方法中的所有字段,将其初始值保存到某个Map<String,Object>
结构中,然后重置@Before中的值或@After方法。以下是使用反射循环遍历类的字段的一些主题:Loop over all fields in a Java class
关于第2点),您可以在此处获得相关说明(涉及类加载器):Java: how to "restart" a static class?
你可以用反射和那些东西做很酷的事情。 :)
答案 2 :(得分:2)
看一下这篇文章:Set Private Static Field。 与BeanInject或ReflectionTestUtils(我经常使用)不同,此机制不需要类的实例。由于这是一个静态字段,我不确定你是否有一个实例。如果您这样做,请使用上述两种方法之一。
从帖子复制:
public static void main(String[] args) throws Exception
{
Field field = MyClass.class.getDeclaredField("woot");
field.setAccessible(true);
field.set(null, "New value");
}
我很惊讶地看到ReflectionTestUtils
需要一个实例。似乎它应该能够处理这种情况。太糟糕了。
正如其他人所说,请在@Before
方法中执行此操作,以确保在测试开始之前的状态。在@After
中这样做是容易出错的,因为它假定您的其他测试可能会影响静态字段的状态。