重置JUnit测试的静态字段

e0uiprwp  于 2022-11-11  发布在  其他
关注(0)|答案(4)|浏览(118)

我有一组JUnit测试,它们调用Java程序上的main方法,传入参数并检查输出。
然而,如果我正在测试的程序有被改变的静态值,它们将在测试之间保持不变。这就产生了一个问题。我无法控制正在测试的程序是什么,或者静态字段使用的名称。
我如何确保我的单元测试干净地运行,就像从头开始程序一样,而不保留那些静态字段。有没有办法以某种方式重置它们?
如果没有,我将不得不启动一个新的进程,运行程序,然后检查输出等,但这似乎有点矫枉过正。
编辑-请注意,我无法控制单元测试正在测试的代码-我无法更改它们的字段名称,不幸的是,我也不知道它们的字段名称。我认为这是不可能的,如果不启动一个新的过程?

ebdffaop

ebdffaop1#

您应该显式地初始化测试类中的任何静态状态,通常这是在注解为@Before@BeforeClass的方法中完成的
这就是为什么在一个应用程序中有很多静态依赖项对于测试来说是一个坏主意的原因,这就是为什么很多人鼓励无状态编程。

sqserrrh

sqserrrh2#

一般来说,如果你发现你的代码是不可测试的,就像这里的问题,这是一个代码气味的迹象,你应该认真考虑重构你的代码,不要使用这些静态字段。
话虽如此,您可能会发现BeanInject library很有帮助。您可以将一个带注解的@After方法放入测试类中,并让它使用注入重置静态字段:

Inject.field("thatStaticField").of(thatObjectWithStaticFields).with("default value");

这样,你只需要知道字段名,而不必实际修改包含字段的类。库使用反射来实现这一点。
此外,我想到,如果你测试的东西包含你无法控制的部分,为什么你不试着用Mockito来模拟这些部分呢?

**EDIT/ADD:**好的,你的问题是你甚至不知道类可能有或没有的静态变量的初始值。我看到两种可能的方法: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?
你可以用反射和那些东西来做很酷的事情。:)

p8ekf7hl

p8ekf7hl3#

看看这个帖子: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中这样做很容易出错,因为它假设其他测试可能会影响静态字段的状态。

nukf8bse

nukf8bse4#

我知道这是一个老问题,但我也遇到过类似的问题,我在方法an@AfterAll中使用“org.mockito.mockito.clearAllCaches”解决了这个问题。
注意:我使用的是junit5。
示例:

// here the static field are beeing mocked
@BeforeAll
static void setUp() {
    mockStatic(Static variable);
}

// here all mocks are beeing excluded, including the static one
@AfterAll
static void reset() {
    clearAllCaches();
}

相关问题