Java 重置 JUnit 测试的静态字段

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26234742/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 02:03:22  来源:igfitidea点击:

Resetting static fields for JUnit tests

javajunit

提问by ThePerson

I have a set of JUnit tests which call the main method on a Java program, passes in args and checks the output. That's fine.

我有一组 JUnit 测试,它们在 Java 程序上调用 main 方法,传入 args 并检查输出。没关系。

However, if the program I am testing has static values which are altered, they will remain the same between tests. This causes a problem. I have no control over exactly what the program is that is being tested, or the names used for static fields.

但是,如果我正在测试的程序具有更改的静态值,它们将在测试之间保持不变。这会导致问题。我无法控制正在测试的程序是什么,或者用于静态字段的名称。

How can I ensure that my Unit tests run cleanly, as if it is starting the program from scratch, without keeping those static fields. Is there a way to reset them in some way?

我如何确保我的单元测试干净地运行,就像它从头开始启动程序一样,而不保留那些静态字段。有没有办法以某种方式重置它们?

If not, I will have to fire up a new process which runs the program, then check the output etc, but this seems a little overkill.

如果没有,我将不得不启动一个运行程序的新进程,然后检查输出等,但这似乎有点矫枉过正。

Edit - Please note I have no control over the code which the unit tests are testing - I can't change their field names, and unfortunately, I also won't know their field names. I am thinking that makes this not possible, without starting a new process?

编辑 - 请注意,我无法控制单元测试正在测试的代码 - 我无法更改它们的字段名称,不幸的是,我也不知道它们的字段名称。我在想,如果不开始一个新的过程,这是不可能的?

采纳答案by ZeroOne

In general if you find your code to be untestable, like is the question here, it's a sign of a code smell, and you should seriously consider refactoring your code to not to use those static fields.

一般来说,如果您发现您的代码无法测试,就像这里的问题一样,这是代码异味的迹象,您应该认真考虑重构您的代码以不使用这些静态字段。

Having said that, you might find the BeanInject libraryhelpful. You could put an @Afterannotated method into your test class and have it reset the static fields using the injection:

话虽如此,您可能会发现BeanInject 库很有帮助。您可以将一个带@After注释的方法放入您的测试类中,并使用注入重置静态字段:

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

That way you only need to know the field names but you don't have to be able to actually modify the class with the fields. The library does that using reflection.

这样你只需要知道字段名称,但你不必能够实际修改带有字段的类。图书馆使用反射来做到这一点。

Additionally, it came to my mind that if you are testing something that contains parts that you cannot control, why don't you try to mock those parts with, say, Mockito?

此外,我想到如果您正在测试包含您无法控制的部分的东西,为什么不尝试使用 Mockito 来模拟这些部分?

EDIT/ADD:OK, so your issue is that you don't even know the initial values of the possible static variables the classes may or may not have. I see two possible approaches: 1) You'd have to either save their values when the class is loaded for the first time and reset the values between each test, or 2) you have to get an entirely new instance of the class from the class loader.

编辑/添加:好的,所以您的问题是您甚至不知道类可能具有或不具有的可能静态变量的初始值。我看到两种可能的方法:1) 您必须在第一次加载类时保存它们的值并在每次测试之间重置值,或者 2) 您必须从类加载器。

On point 1), you'd need to use reflection to loop through all the fields in your @BeforeClass method, save their initial values into some Map<String,Object>structure, and then reset the values in your @Before or @After method. Here's some topic on looping through the fields of a class using reflection: Loop over all fields in a Java class

在第 1) 点上,您需要使用反射循环遍历 @BeforeClass 方法中的所有字段,将它们的初始值保存到某个Map<String,Object>结构中,然后重置 @Before 或 @After 方法中的值。这里有一些关于使用反射循环遍历类的字段的主题:循环遍历 Java 类中的所有字段

Regarding point 2), you have the instructions for that (involving class loaders) here: Java: how to "restart" a static class?

关于第 2 点),您在此处有相关说明(涉及类加载器):Java:如何“重新启动”静态类?

It's pretty cool what you can do with reflection and that stuff. :)

你可以用反射和那些东西做的事情很酷。:)

回答by Joel

You should explicitly initialize any static state within your test classes, usually this is done in methods annotated @Beforeor @BeforeClass

您应该显式初始化测试类中的任何静态状态,通常这是在注释@Before@BeforeClass

This is a reason, among others, why having a lot of static dependencies in an application is a bad idea for testing. That's why many people encourage stateless programming.

这就是为什么在应用程序中拥有大量静态依赖项对于测试来说是个坏主意的原因之一。这就是为什么许多人鼓励无状态编程的原因。

回答by John B

Take a look at this post: Set Private Static Field. Unlike BeanInject or ReflectionTestUtils(which I use a lot), this mechanism does not require a instance of the class. Since this is a static field I wasn't sure if you had an instance. If you do, use one of the two above.

看看这篇文章:设置私有静态字段。与 BeanInject 或ReflectionTestUtils(我经常使用)不同,这种机制不需要类的实例。由于这是一个静态字段,我不确定您是否有实例。如果这样做,请使用上述两种方法之一。

Copied from post:

从帖子复制:

 public static void main(String[] args) throws Exception
{
    Field field = MyClass.class.getDeclaredField("woot");
    field.setAccessible(true);
    field.set(null, "New value");
}

I was surprised to see that ReflectionTestUtilsrequired an instance. Seems like it should be able to handle this case. Too bad.

我很惊讶地看到这ReflectionTestUtils需要一个实例。似乎它应该能够处理这种情况。太糟糕了。

As others have stated, do this in the @Beforemethod to ensure state BEFORE your test begins. Doing so in the @Afteris error prone as it assumes that you other test might affect the state of the static field.

正如其他人所说,在@Before方法中执行此操作以确保测试开始之前的状态。这样做很@After容易出错,因为它假设您的其他测试可能会影响静态字段的状态。