Replacing PowerMock's @PrepareForTest programmatically?

John Q Citizen picture John Q Citizen · Oct 29, 2013 · Viewed 15.2k times · Source

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

Answer

Johan picture Johan · Nov 1, 2013

Perhaps you're looking for a mock policy?