31

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).

Gijs
  • 144
  • 1
  • 1
  • 11
John Q Citizen
  • 3,138
  • 4
  • 26
  • 31

4 Answers4

1

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

René Link
  • 48,224
  • 13
  • 108
  • 140
0

Perhaps you're looking for a mock policy?

falconepl
  • 398
  • 1
  • 3
  • 15
Johan
  • 37,479
  • 32
  • 149
  • 237
  • Thanks for the reply. I investigated `MockPolicy`, but as I noted above it only works with `PowerMockRunner` and not with `PowerMockRule`. I need to use `PowerMockRule` because of class loader issues, and the `MockPolicy` initializer only works with `MockClassLoader` provided by `PowerMockRunner`. – John Q Citizen Nov 03 '13 at 22:57
0

Could you help this (taken from documentation)?

You can also prepare whole packages for test by using wildcards:

@PrepareForTest(fullyQualifiedNames="com.mypackage.*")

So you can add the whole library to your prepare...

Community
  • 1
  • 1
Renato
  • 2,077
  • 1
  • 11
  • 22
-1

Why do you even want to mock static methods? Why not wrap those static methods in a class that you can mock with mockito?

class FooWraper {
   void someMethod() {
     Foo.someStaticMethod()
   }
}

and then you can create a mock of your FooWraper. No need to use Powermock at all...

Marcin Grzejszczak
  • 10,624
  • 1
  • 16
  • 32
  • I just ran into the same issue and here's my motivation: Lombok generates static `builder` methods, and I'd like to test if all fields of a builder have been set (they are optional, so this is to prevent accidental omission when new parameters are added). To do this, I am mocking the static `builder` method to return a CGLIB proxy, with which I then count if all `set…` methods have been invoked. However, this adds four extra lines to each test: one for `@PrepareForTest` and another three for static block with `PowerMockAgentTestInitializer.initialize`. This question would solve my first issue. – Jezor Nov 16 '21 at 12:36