6

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?

Update: using reflection doesn't seem to work, either:

@Test
public void testGet_usingReflection() throws Exception {
    setField(Whitebox.invokeConstructor(Foo.class), "FLAG", true);
    assertTrue(Foo.get());
}

public static void setField(Object targetObject, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
    Class clazz =  targetObject.getClass();
    Field field = clazz.getDeclaredField(fieldName);
    field.setAccessible(true);
    Field modifiers = Field.class.getDeclaredField("modifiers");
    modifiers.setAccessible(true);
    modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    field.set(targetObject, value);
}

The setField() method is one I have available through an internal library. Unfortunately, it produces the same result: AssertionError

Update 2: Getting completely rid of PowerMock doesn't help much, either, apparently:

@Test
public void testGet_usingReflectionWithoutPowerMock() throws Exception {
    setField(Foo.class.getDeclaredField("FLAG"), true);
    assertTrue(Foo.get());
}

public static void setField(Field field, Object value) throws NoSuchFieldException, IllegalAccessException {
    field.setAccessible(true);
    Field modifiers = Field.class.getDeclaredField("modifiers");
    modifiers.setAccessible(true);
    modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    field.set(null, value);
}

For this, I even removed the PowerMock annotations from the class level...

I've also posted this question to the PowerMock mailing list now.

Christian
  • 6,070
  • 11
  • 53
  • 103
  • did you find any solution for this? – amod Jan 31 '18 at 22:37
  • No. I ended up changing the constant from using primitive `boolean`to object wrapper `Boolean`, I think. – Christian Jan 31 '18 at 23:36
  • I am using Wrapper Integer but it's not working for me. :( – amod Feb 01 '18 at 15:16
  • Check your generated classes in your target directory using a [decompiler](http://jd.benow.ca/) to verify what's actually going on, and if the compiler [inlined](https://stackoverflow.com/questions/33281347/how-do-i-set-a-primitive-private-static-final-field-in-a-class-with-private-cons?noredirect=1#comment54866343_33508845) the value somewhere. If not, [Ask a Question](https://stackoverflow.com/questions/ask) and provide (as much as necessary and as little as possible) detail of your classes and tests. :) – Christian Feb 01 '18 at 21:14

2 Answers2

2

I had similar thing to test, but the only difference was that my static final variable was an Object rather than primitive type. And indeed changing boolean to Boolean makes it work.

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

Also I would recommend using @PrepareForTest(SomeClass.class) where SomeClass has static final field rather than @PrepareEverythingForTest because if it's big project it may take some time prepare everything.

Templar
  • 1,843
  • 7
  • 29
  • 42
  • 2
    I found the answer why reflection can't change the primitive field's value here: http://stackoverflow.com/a/14102109/2018047 The value is inlined by the compiler, so there is a constant in the code wherever there is a reference to it. I can change the field using reflection, it is just never referenced at runtime... Still not sure why PowerMock, which manipulates byte code, can't do it, though. – Christian Nov 05 '15 at 11:18
0

WhiteBox.setInternalState does not work with class with a final field.

You can use Reflection to resolve this issue:

static void setFinalStatic(Field field, Object newValue) throws Exception {  
  field.setAccessible(true);  

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

  field.set(null, newValue);  
}  

Source: https://code.google.com/p/powermock/issues/detail?id=324

Maxime
  • 9,638
  • 4
  • 17
  • 17
  • 3
    Well, according to [this post](https://groups.google.com/d/msg/powermock/SfQygHwGeGY/SKsigfkiFS8J) in the PowerMock group, `Whitebox` should be able to set `final` fields, provided the class is prepared for test. I also tried reflection, with pretty much the same result (i.e. not wroking). I updated the post accordingly. – Christian Oct 22 '15 at 13:24