Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resetting static fields for JUnit tests

Tags:

java

junit

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.

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?

like image 778
ThePerson Avatar asked Oct 07 '14 11:10

ThePerson


2 Answers

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 library helpful. You could put an @After annotated method into your test class and have it reset the static fields using the injection:

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?

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.

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

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

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

like image 160
ZeroOne Avatar answered Sep 24 '22 14:09

ZeroOne


You should explicitly initialize any static state within your test classes, usually this is done in methods annotated @Before or @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.

like image 22
Joel Avatar answered Sep 26 '22 14:09

Joel