Resetting static fields for JUnit tests

后端 未结 3 429
迷失自我
迷失自我 2021-01-11 15:25

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 ha

相关标签:
3条回答
  • 2021-01-11 15:56

    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.

    0 讨论(0)
  • 2021-01-11 16:09

    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. :)

    0 讨论(0)
  • 2021-01-11 16:10

    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.

    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 required an instance. Seems like it should be able to handle this case. Too bad.

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

    0 讨论(0)
提交回复
热议问题