26

what is the absolute minimal mocking that must be done to pass this test?

code:

class PrivateStaticFinal {
    private static final Integer variable = 0;
    public static Integer method() { return variable + 1; }
}

test:

@RunWith(PowerMockRunner.class)
@PrepareForTest(PrivateStaticFinal.class)
class PrivateStaticFinalTest {
    @Test
    public void testMethod() {
        //TODO PrivateStaticFinal.variable = 100
        assertEquals(PrivateStaticFinal.method(), 101);
    }
}

related: Mock private static final variables in the testing class (no clear answer)

Community
  • 1
  • 1
sam boosalis
  • 1,997
  • 4
  • 20
  • 32
  • 1
    These statics are all in the same class which makes it tricky. All the issues I have run into are using statics from other places when trying to test a class. Having to mock partial parts of the class you are trying to test just seems bad. You should never be mocking the class you are testing, just things it is depending on. – Walls Apr 21 '14 at 14:33

1 Answers1

41

Disclaimer: After a lot of hunting around on various threads I have found an answer. It can be done, but the general concensus is that it is not very safe but seeing as how you are doing this ONLY IN UNIT TESTS, I think you accept those risks :)


The answer is not Mocking, since most Mocking does not allow you to hack into a final. The answer is a little more "hacky", where you are actually modifying the private field when Java is calling is core java.lang.reflect.Field and java.lang.reflect.Modifier classes (reflection). Looking at this answer I was able to piece together the rest of your test, without the need for mocking that solves your problem.

The problem with that answer is I was running into NoSuchFieldException when trying to modify the variable. The help for that lay in another post on how to access a field that was private and not public.

Reflection/Field Manipulation Explained:

Since Mocking cannot handle final, instead what we end up doing is hacking into the root of the field itself. When we use the Field manipulations (reflection), we are looking for the specific variable inside of a class/object. Once Java finds it we get the "modifiers" of it, which tell the variable what restrictions/rules it has like final, static, private, public, etc. We find the right variable, and then tell the code that it is accessible which allows us to change these modifiers. Once we have changed the "access" at the root to allow us to manipulate it, we are toggling off the "final" part of it. We then can change the value and set it to whatever we need.

To put it simply, we are modifying the variable to allow us to change its properties, removing the propety for final, and then changing the value since it is no longer final. For more info on this, check out the post where the idea came from.

So step by step we pass in the variable we want to manipulate and...

// Mark the field as public so we can toy with it
field.setAccessible(true);
// Get the Modifiers for the Fields
Field modifiersField = Field.class.getDeclaredField("modifiers");  
// Allow us to change the modifiers
modifiersField.setAccessible(true);
 // Remove final modifier from field by blanking out the bit that says "FINAL" in the Modifiers
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
// Set new value
field.set(null, newValue); 

Combining this all into a new SUPER ANSWER you get.

@RunWith(PowerMockRunner.class)
@PrepareForTest()
class PrivateStaticFinalTest {
    @Test
    public void testMethod(){
      try {
        setFinalStatic(PrivateStaticFinal.class.getDeclaredField("variable"), Integer.valueOf(100));
      } 
      catch (SecurityException e) {fail();}
      catch (NoSuchFieldException e) {fail();}
      catch (Exception e) {fail();}
      assertEquals(PrivateStaticFinal.method(), Integer.valueOf(101));
    }

    static void setFinalStatic(Field field, Object newValue) throws Exception {
        field.setAccessible(true);
        // remove final modifier from field
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, newValue);
    }
}

Update The above solution will work only for those constants which is initialized in static block.When declaring and initializing the constant at the same time, it can happen that the compiler inlines it, at which point any change to the original value is ignored.

Mayank Gupta
  • 93
  • 1
  • 11
Walls
  • 3,972
  • 6
  • 37
  • 52
  • could you explain what ` Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); field.set(null, newValue); ` does? – sam boosalis Apr 22 '14 at 20:04
  • Edited the answer up a bit with some more explaning done, as well as a link to the original article which was the source of the idea to "modifying" the "modifiers" to allow us to hack into the variable itself and remove the finality of it. – Walls Apr 22 '14 at 20:36
  • thanks! i want the test code to still respect the source code, so will a `modifiersField.setInt(field, field.getModifiers() & Modifier.FINAL); modifiersField.setAccessible(false);` work after we set the value we want. i.e. a "do/undo" thing. – sam boosalis Apr 22 '14 at 20:48
  • also, am i right in saying that we're setting the private field with "normal" reflection, but we must make it not final first, with more reflection? – sam boosalis Apr 22 '14 at 20:49
  • 1
    You are basically seeing the variable at runtime, and through reflection are allowed to modify it. Changing things back should still work (it did when I tested), but I think at that point you are too far gone already. The source isn't getting changed, what runtime sees is getting changed. It's very messy, but seems to be the only way to modify the `static final` without changing it in the source code to reflect test data. For a little more info, check out [this answer](http://stackoverflow.com/questions/37628/what-is-reflection-and-why-is-it-useful/17531269#17531269). – Walls Apr 22 '14 at 20:59
  • i know the source isn't being change. and assigning to final is a compiler error. but i'd like the (runtime) code being tested to be the same as the code ones reads. – sam boosalis Apr 22 '14 at 21:03
  • 4
    **Attention**: this only works reliably when the constant is created in a static block. When declaring and initializing the constant at the same time, it can happen that the compiler inlines it, at which point any change to the original value is ignored. – DJGummikuh Jul 06 '15 at 05:50
  • This didn't work for me and I had to update the `setFinalStatic` method to be: `static void setFinalStatic(Field field, Object newValue, final Object targetObject)` and the last line of that method to: `field.set(targetObject, newValue);`, otherwise I was getting NullPointer Exceptions. Not sure if this is Java version specific or just me doing something stupid :P – Zathrus Writer Jun 24 '17 at 12:34
  • 1
    This won't work if you are trying to modify a field of primitive data type. I just spent half a day to realize this. – Kedarnath May 19 '21 at 12:24
  • @Kedarnath Thanks for the comment. I ran into the dame issue where I couldn't reset it for primitive variables. – Aditya Feb 21 '22 at 20:55