Is there a way to reinitialize a static class in J

2019-04-06 03:50发布

I'm trying to unit test a class that references static data from another class. I cannot "not" use this static class, but obviously running multiple tests has become problematic. So my question is this. Is there a way in a junit test to reinitialize a static class? That way one test is not effected by a previous test?

So in other words some way of doing this:

Foo.setBar("Hello");

// Somehow reinitialize Foo

String bar = Foo.getBar(); // Gets default value of bar rather than "Hello"

Unfortunately, I cannot change Foo, so I'm stuck using it.

Edit It appears I made my example a bit too simple. In the real code "Bar" is set by a system property and gets set to an internal static variable. So once it starts running, I can't change it.

7条回答
成全新的幸福
2楼-- · 2019-04-06 03:58

You could use PowerMock (with Mockito) or JMockit to mock the static class to have it do whatever you want in each test.

查看更多
劳资没心,怎么记你
3楼-- · 2019-04-06 03:58

Three suggestions,

  1. Call the static method from the @Before setting it to some known value.

  2. Use ReflectionTestUtils to set the value via reflection.

  3. Update your code to have a instance wrapper class that wraps the call to the static method in an instance method / class. Mock the wrapper and inject into your class under test.

查看更多
Evening l夕情丶
4楼-- · 2019-04-06 03:59

I would use Factory pattern with init and destroy static methods that should take care about all instances.

Something like:

public class FooFactory {

  private static Foo mFoo  = null;

  public static Foo init(){

      if(mFoo == null){
          mFoo = new Foo();
      }
      return mFoo;
  }

  public static void destroy(){
      if(mFoo != null){
          mFoo = null;
      }
  } 
}

So per unitest enough to run:

FooFactory.init();// on start

....

FooFactory.destroy();// on finish
查看更多
劫难
5楼-- · 2019-04-06 04:10

If you use PowerMock, you can mock static methods -- which is what you should do.

查看更多
手持菜刀,她持情操
6楼-- · 2019-04-06 04:13

Here is a little example where a utility class using static initializer is re-loaded to test initialization of that utility. The utility uses a system property to initialize a static final value. Normally this value cannot be changed at runtime. So the jUnit-test reloads the class to re run the static initializer…

The utility:

public class Util {
    private static final String VALUE;

    static {
        String value = System.getProperty("value");

        if (value != null) {
            VALUE = value;
        } else {
            VALUE = "default";
        }
    }

    public static String getValue() {
        return VALUE;
    }
}

The jUnit-test:

import static org.junit.Assert.assertEquals;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.junit.Test;

public class UtilTest {

    private class MyClassLoader extends ClassLoader {

        public Class<?> load() throws IOException {
            InputStream is = MyClassLoader.class.getResourceAsStream("/Util.class");

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int b = -1;

            while ((b = is.read()) > -1) {
                baos.write(b);
            }

            return super.defineClass("Util", baos.toByteArray(), 0, baos.size());
        }
    }

    @Test
    public void testGetValue() {
        assertEquals("default", getValue());
        System.setProperty("value", "abc");
        assertEquals("abc", getValue());
    }

    private String getValue() {
        try {
            MyClassLoader myClassLoader = new MyClassLoader();
            Class<?> clazz = myClassLoader.load();
            Method method = clazz.getMethod("getValue");
            Object result = method.invoke(clazz);
            return (String) result;
        } catch (IOException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
            throw new IllegalStateException("Error at 'getValue': " + e.getLocalizedMessage(), e);
        }
    }
}
查看更多
乱世女痞
7楼-- · 2019-04-06 04:19

Though it was a bit dirty, I resolved this by using reflections. Rather than rerunning the static initializer (which would be nice), I took the fragile approach and created a utility that would set the fields back to known values. Here's a sample on how I would set a static field.

final Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
final Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

field.set(null, value);
查看更多
登录 后发表回答