How do I set a private static final field in a class with private constructor using PowerMockito?

6,431 views
Skip to first unread message

Christian Balzer

unread,
Oct 22, 2015, 8:55:05 AM10/22/15
to PowerMock

Hi all,


I'm trying to set a private static final field in a class with private constructor for a JUnit test. When I boil the code down to its basics, I get the following:

public class Foo {
    private static final boolean FLAG = false;
    private Foo() { /* don't call me */  }
    public static boolean get() { return FLAG; }
}


My tests looks like this:

@RunWith(PowerMockRunner.class)
@PrepareEverythingForTest  // Whitebox fails without this line
public class FooTest {
    @Test
    public void testGet() {
        Whitebox.setInternalState(Foo.class, "FLAG", true);
        assertTrue(Foo.get());
    }
}


And here is an excerpt from my POM file:

<junit.version>4.11</junit.version>
<powermock.version>1.5.4</powermock.version>
<mockito.version>1.9.5</mockito.version>


When I put a breakpoint on return FLAG;, I can clearly see that FLAG was set to true in IntelliJ's debugger. Yet the test fails with an AssertionError.


Any ideas what to do to make this work?


Kind regards,


Christian

P.S.: This is a cross-post from StackOverflow: http://stackoverflow.com/q/33281347/2018047 

Johan Haleby

unread,
Oct 26, 2015, 10:18:21 AM10/26/15
to powe...@googlegroups.com
You need to suppress the static constructor first and for that to work you need to use the PowerMockRunner and prepare Foo for test.

--
You received this message because you are subscribed to the Google Groups "PowerMock" group.
To unsubscribe from this group and stop receiving emails from it, send an email to powermock+...@googlegroups.com.
To post to this group, send email to powe...@googlegroups.com.
Visit this group at http://groups.google.com/group/powermock.
For more options, visit https://groups.google.com/d/optout.

Christian Balzer

unread,
Nov 5, 2015, 7:53:30 AM11/5/15
to PowerMock
Hi Johan,

I tried to add the annotation @SuppressStaticInitializationFor to suppress the static initializer, but the test still fails:

@RunWith(PowerMockRunner.class)
@PrepareEverythingForTest // including foo.Foo, the class under test
@SuppressStaticInitializationFor("Foo") // foo.Foo is in the default package
public class FooTest {
@Test
public void testGet() {
Whitebox.setInternalState(Foo.class, "FLAG", true);
assertTrue(Foo.get());
}
}

I also tried adding a field of type int and assigned it to 5.
It stays at 5, which means the problem is not that FLAG got assigned boolean's default value, false. Unfortunately, it also implies suppressing the static constructor was somewhat unsuccessful. Any idea where I'm going wrong?

On StackOverflow, someone suggested the test works if FLAG is of object type Boolean instead of of primitive type boolean. Elsewhere on StackOverflow, someone said the problem was that the Java compiler inlined the value of primitives, if certain conditions are met (i.e. when the expression is "a 'constant expression' as defined by the JLS"). So, the only way to get this test to pass would be to change the value before inlining occurs. The question is, how? Suppressing the static initializer is probably one step too late, I guess?

Brgrds,

Christian

Johan Haleby

unread,
Nov 5, 2015, 8:29:27 AM11/5/15
to powe...@googlegroups.com
Sorry my bad. It's not possible unless the static field is initialized in a static constructor. For example:

private static final String string;

static {
    string = "something";
}

However it might be possible using sun.misc.Unsafe but I wouldn't recommend that route. I think I managed to get this working using the Objensis Classloader Deepcloner (see UnsafeFieldWriter in that class).
Reply all
Reply to author
Forward
0 new messages