I am using PowerMock to mock static methods in junit tests, typically done as follows:
@RunWith(PowerMockRunner.class) @PrepareForTest({Foo.class,Bar.class}) public class SomeUnitTest { @Before public void setUpTest() { setUpFoo(); setUpBar(); } private void setUpFoo() { mockStatic(Foo.class); when(Foo.someStaticMethod()).thenReturn(1); } private void setUpBar() { mockStatic(Bar.class); when(Bar.someStaticMethod()).thenReturn(2); } @Test public void someTestCase() { ... } }
This works fine, but I'm finding that specifying the @PrepareForTest
annotation is preventing me from making my testing API flexible.
What I'd like to do is something like the following:
public class MockLibraryOne { public static void setUpLibraryOne() { setUpFoo(); setUpBar(); } private static void setUpFoo() { mockStatic(Foo.class); when(Foo.someStaticMethod()).thenReturn(1); } private static void setUpBar() { mockStatic(Bar.class); when(Bar.someStaticMethod()).thenReturn(2); } } @RunWith(PowerMockRunner.class) public class SomeUnitTest { @Before public void setUpTest() { MockLibraryOne.setUpLibraryOne(); } @Test public void someTestCase() { ... } }
Here my unit test has a dependency on LibraryOne
, but it does not know which classes LibraryOne
depends on, so it does not know which classes to add to the @PrepareForTest
annotation.
I could make SomeUnitTest
extend MockLibraryOne
and add the @PrepareForTest
annotation to the MockLibraryOne
class, but I will have dependencies on more than just MockLibraryOne
in other unit tests, so inheritance is not a general solution.
Is there some way of programmatically preparing a class for testing under PowerMock, instead of using the @PrepareForTest
annotation? For example, something like the following:
public class MockLibraryOne { public static void setUpLibraryOne() { setUpFoo(); setUpBar(); } private static void setUpFoo() { prepareForTest(Foo.class); mockStatic(Foo.class); when(Foo.someStaticMethod()).thenReturn(1); } private static void setUpBar() { prepareForTest(Bar.class); mockStatic(Bar.class); when(Bar.someStaticMethod()).thenReturn(2); } }
I guess it would be nice if PowerMockRunner
processed the @PrepareForTest
annotation a little differently: for each specified class, it should not only add that class (and its hierarchy) to the list of classes to prepare for mocking, but then examine that class to see if it has any @PrepareForTest
annotations as well:
@RunWith(PowerMockRunner.class) @PrepareForTest({MockLibraryOne.class}) public class SomeUnitTest { ... } @PrepareForTest({Foo.class,Bar.class}) public class MockLibraryOne { ... }
}
So in this the @PrepareForTest
annotation on SomeUnitTest
would find MockLibraryOne
, and the @PrepareForTest
annotation there would drag in Foo.class
and Bar.class
as well.
So perhaps writing my own test runner to replace PowerMockRunner
may be a solution.
Or perhaps there's a simpler solution, using PowerMockAgent
class, for example?
edit: Mock Policies may be one solution: https://code.google.com/p/powermock/wiki/MockPolicies
edit: Mock Policies works with PowerMockRunner
but not (it seems) with PowerMockRule
(which I sometimes require due to class loader issues).
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With