Skip to main content
Blog

Mocking static methods with Powermockito

By 23 december 2013januari 30th, 2017No Comments

Mocking frameworks have become crucial in the area of junit testing. Generally mocking frameworks have a hard time mocking static or final methods. You should setup your architecture in such a way that it facilitates easy unit testing. However, this is not always possible. You generally don’t get the time to restructure a big part of an application. Furthermore sometimes static methods must be used because it is part of a third party codebase.

 

Practical Problem

I encountered a similar problem a bean had to be injected a non-CDI part of our application. We had to have an instance of a class available that was highly dependent on the JEE context. Normally you would inject the instance using @Inject. However, this was not possible because the JEE context was not present.

Let me illustrate the problem with an example. Suppose you have the following Reporter class.

public class Reporter {
    OutputStream outputStream;

    public Reporter(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    public String reportMessage(String message) throws IOException {
        PrintWriter pw = new PrintWriter(outputStream);
        pw.println(message);
    }
}

Suppose A change is requested that the context information should also be written to the output stream. This context information can only be retrieved with the ContextManager class using the jee context. The ContextManager class is specified as follows.

public final class ContextManager() {
    public static String getContext() {
        ...
    }
}

Because of time limitations and minimizing the issues that would pop up during merging, we cannot refactor the Reporter class. The Reporter class would look like this including the context manager call.

public class Reporter {
    OutputStream outputStream;

    public Reporter(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    public String reportMessage(String message) throws IOException {
        PrintWriter pw = new PrintWriter(outputStream);
        pw.println(String.format("%s : %s", ContextManager.getContext(), message));
    }
}

Whenever the context manager cannot retrieve the jee context, it will throw an exception. In standard unit tests you cannot test the behavior, because you will always get an (EJB)Exception.

 

Using PowerMockito

To be able to cope with such a static method call, we used PowerMockito. You can apply it like this:

@RunWith(PowerMockitoRunner.class)
@PrepareForTest(ContextManager.class)
public class statTest {

    @Before
    public void prepare() {
        PowerMockito.mockStatic(ContextManager.class);
        PowerMockito.when(ContextManager.getContext()).thenReturn("something");
    }

    ... your test code here
}

This makes it possible to mock the static class. However, in the documentation of PowerMockito it is stated that you should not do this. We did it anyway, but it had some consequences.

  • You cannot use the Mockito.mock() method in the Test class constructor.
  • Powermockito replaces the used classloader to solve the static issue. This classloader is very slow and cannot handle all classes (for example Classes that have been annotated with JAXB annotations). We had a testsuite that normally took 1 minute. With powermock the same suite took 40 minutes!
    In other words, good bye continuous integration!
  • Some tests failed unexpectedly and we couldnĀ“t explain why.

The first issue is easily overcome by using the @Mock annotation (also specifying the @RunWith(MockitotJUnitRunner.class)).

You can use the @PowerMockIgnore annotation to solve some of the class loading issues and the unit test performance. This annotation mainly says that powermock should ignore specific classes and/or packages. Ignoring means here that the normal Classloader is used for those classes. This solution is however not a structural one. Newly added classes could influence the performance of arbitrary unit tests in your test suite.

The last problem we solved by doing some refactoring, omitting the need to mock a static method.

When someone would ask me whether the mocking of static methods is an option, I would say no, unless you don’t have the time to refactor the involved code. In all other cases I advise refactoring.

 

Simple Refactor strategy

 

A simple way to avert the static method problem in your production code unit tests is to encapsulate the static call in a non-static call. For example consider the following code:

class A {
    public void doSomething() {
        SomethingExecutor executor = B.lookup();
        executor.something(1);
        executor.something(2);
    }
}

Create a class C

class C {
    public SomethingExecutor getExecutor() {
        return B.lookup();
    }
}

You can now refactor you class A to the following:

class A {
    private C c;

    public void doSomething() {
        SomethingExecutor executor = getC().getExecutor();
        executor.something(1);
        executor.something(2);
    }

    C getC() {
        if (c == null) {
            c = new C();
        }
        return c;
    }

    void setC(C c) {
        this.c = c;
    }
}

You can now use the setC() method in your unit test to set a standard mock object from your junit test. If you can use injection in your class, you can ommit the lazy loading in the getC() method.
If you want to unit test class C, you must still use powermockito, but at least the impact is limited to the encapsulating class C.

Of course it would be ideal to eliminate the static method altogether, but as said before it depends on effort and impact whether that is an option.