Replacing PowerMock’s @PrepareForTest programmatically?

What you try to achieve will not work.

The problem is that powermock must rewrite the client class’s code to intercept the static invocation and it can’t do this after the class is loaded. Thus it can only prepare a class for test before it is loaded.

Let’s assume you want to mock the System.currentTimeMillis invocation in the following simple class.

class SystemClock {
    public long getTime() {
        return System.currentTimeMillis();
    }
}

Powermock will not change the code of java.lang.System.currentTimeMillis, because it can’t. Instead it changes the SystemClock‘s byte code so that it does not invoke System.currentTimeMillis anymore. Instead it invokes some other object that belong to powermock.

This is how powermock get’s full control over the return value and allows you to write a test like this:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ SystemClock.class })
public class PowerMockitoTest {

    @Test
    public void systemTimeMillis() {
        SystemClock systemClock = new SystemClock();

        PowerMockito.mockStatic(System.class);

        PowerMockito.when(System.currentTimeMillis()).thenReturn(12345L);

        long time = systemClock.getTime();
        assertEquals(12345L, time);
    }
}

You can see that powermock has rewritten the client class in the stacktrace of your debugger. Set a breakpoint at SystemClock.getTime and step into the invoked method.

Powermock stacktrace

As you can see SystemClock invokes a MockGateway.

If you take a look at the variables on the stack of the MockGateway invocation, you can see how the original System.currentTimeMillis method is handled.

debug variables

Leave a Comment