Using PowerMock + TestNG to Mock a Static Class

written in java, mocking, testing

 This week I needed to test a class that depended on a method from an static class. I saw we were using PowerMock and thought to myself: “Well this sounds pretty common, I bet it’s easy to accomplish”. But of course I ran into half a dozen issues before I was able to make it work. Here’s my two cents to make your experience easier than mine.

Setup

Let’s start with the ingredients. To mock static methods you’ll need a couple of libraries:

When choosing your library version you’ll need to make sure PowerMock and TestNG versions are compatible. You can do so by comparing your versions with the ones specified here.

Also, if you’re not using Maven to include PowerMock in your project make sure you also add it’s dependencies. You’ll find a zip file containing everything you need here.

Writing the test

To have the test working you’ll need to do 3 things:

  1. Configure TestNG to use the PowerMock object factory
  2. Use @PrepareForTest annotation to prepare the static class
  3. Mock the static class method
  4. Write the rest of the test

Let’s go one by one:

1. Configure TestNG to use the PowerMock object factory

There are a bunch of ways of doing this, namely:

  • Configure it on the suite.xml file
  • Extending your test class with PowerMockTestCase
  • Or by adding a method like this to your test
1
2
3
4
@ObjectFactory
public IObjectFactory getObjectFactory() {
  return new org.powermock.modules.testng.PowerMockObjectFactory();
}

I choose to go with the latter because I don’t use the suite.xml file and adding an annotated method is less restrictive than extending a class. But feel free to use whatever works for you.

2. @PrepareForTest

You’ll need to prepare your static class for mocking. You can do so with the @PrepareForTest annotation like this:

1
2
3
4
@PrepareForTest(StaticHelper.class)
public class MyTest {
  ...
}

Note that you can pass an array of classes to the annotation if you need to prepare multiple classes.

3. Mocking

Now you’re ready to mock the static method like this:

1
2
3
4
5
6
7
8
@Test
public void test() throws Exception {
  // mocking static method
  PowerMock.mockStatic(StaticHelper.class);
  EasyMock.expect(StaticHelper.doSomething()).andReturn(hello world)).anyTimes();
  PowerMock.replay(StaticHelper.class);
  ...
}

4. Writing the rest

Ok let’s put everything together and write the rest of the test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@PrepareForTest(StaticHelper.class)
public class MyTest {

  @ObjectFactory
  public IObjectFactory getObjectFactory() {
      return new org.powermock.modules.testng.PowerMockObjectFactory();
  }

  @Test
  public void test() throws Exception {
      // mocking static method
      PowerMock.mockStatic(StaticHelper.class);
      EasyMock.expect(StaticHelper.doSomething()).andReturn(hello world)).anyTimes();
      PowerMock.replay(StaticHelper.class);
  
      // test
      Assert.assertEquals(hello world  StaticHelper.doSomething());
  }

}

Of course this is an oversimplified example. The cool thing about mocking static methods is that the static call you may need to mock may be hidden under several layers of abstraction. Using this approach you are able to mock the static call and test your classes without changing a single line of production code.

Some things to watch out for

There are a few things to keep in mind when initializing the mock:

  1. You cannot create mocks during field initialization.
  2. You cannot create mocks inside before static methods.

Finally I also run into the following error when running my test:

1
java.lang.VerifyError: Expecting a stackmap frame at branch target 71 in method com.abc.domain.myPackage.MyClass$JaxbAccessorM_getDescription_setDescription_java_lang_String.get(Ljava/lang/Object;)Ljava/lang/Object; at offset 20_

Turns out that, as explained here Java 7 introduced a stricter verification and changed the class format. The byte code generation library PowerMock uses is generating code that does not comply with the new verification. But worry not, this validation can be disabled by passing -noverify as argument to the JVM.

If you’re running you’re using Maven to run your tests remember to add the argument to your plugin configuration.


  1. This guide uses EasyMock but you can also use Mockito


Comments