Resetting static fields for JUnit tests

I have a suite of JUnit tests that call a main method in a Java program, pass in args, and check the output. It's fine.

However, if the program I am testing has static values ​​that change, they will stay the same between tests. This is causing the problem. I have no control over what the program is testing, or the names used for static fields.

How can I ensure that my unit tests run cleanly, as if it were running the program from scratch, without storing those static fields. Is there a way to reset them somehow?

If not, I will need to start a new process that starts the program, then checks the output, and so on, but this seems a bit overwhelming.

Edit - Please note: I have no control over the code that tests the unit tests - I cannot change the names of my fields, and unfortunately I will not know their field names either. I think this is not possible without starting a new process?

+3


source to share


3 answers


In general, if you find that your code is not testable, like the question here is, it is a sign of a code smell and you should seriously consider refactoring your code not to use these static fields.

Having said that, you can find the BeanInject library . You can put @After

an annotated method in the test class and reset static fields using injection:

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

      

This way you need to know the field names, but you don't need to be able to change the class using fields. The library does this with reflection.

Also, it occurred to me that if you're testing something that contains parts you can't control, why don't you try to mock those parts with, say, Mockito?



EDIT / ADD: OK, so the problem is that you don't even know the initial values ​​of possible static variables that the classes may or may not have. I see two possible approaches: 1) You either have to store your values ​​when the class is loaded the first time and reset the values ​​between each test, or 2) you have to get a completely new instance of the class from the classloader.

In point 1), you will need to use reflection to loop through all the fields of your @BeforeClass method, store their initial values ​​in some structure Map<String,Object>

, and then reset the values ​​in the @Before or @After method. Here are some topics about looping through class fields using reflection: Loop over all fields in a Java class

Regarding point 2), you have instructions for doing this (involving classloaders) here: Java: how to "restart" a static class?

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

+2


source


You must explicitly initialize any static state in your test classes, this is usually done in methods annotated @Before

or@BeforeClass



This is one of the reasons, among other things, why having many static dependencies in an application is a bad idea for testing. This is why many people encourage stateless programming.

+6


source


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

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 ReflectionTestUtils

an instance is required. It looks like he should be able to handle this matter. Too bad.

As others have stated, do this in a method @Before

to provide pre-test state. Doing this in @After

is error-prone as it suggests that another test might affect the state of the static field.

+2


source







All Articles