Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to reinitialize a static class in Java?

Tags:

java

junit

static

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.

like image 628
Jason Thompson Avatar asked Oct 10 '13 17:10

Jason Thompson


People also ask

Can we reinitialize static variable in Java?

Declaring variables only as static can lead to change in their values by one or more instances of a class in which it is declared. Declaring them as static final will help you to create a CONSTANT. Only one copy of variable exists which can't be reinitialize.

Can we're initialize static variable?

If you declare a static variable in a class, if you haven't initialized it, just like with instance variables compiler initializes these with default values in the default constructor. Yes, you can also initialize these values using the constructor.

Can we're assign value to static variable in Java?

In Java, non-static final variables can be assigned a value either in constructor or with the declaration. But, static final variables cannot be assigned value in constructor; they must be assigned a value with their declaration.


2 Answers

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);
like image 184
Jason Thompson Avatar answered Sep 18 '22 19:09

Jason Thompson


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);
        }
    }
}
like image 33
André Avatar answered Sep 19 '22 19:09

André