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?
source to share
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. :)
source to share
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.
source to share
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.
source to share