PowerMock with unsupported frameworks such as JMock

Currently PowerMock builds on top of EasyMock and Mockito to provide mocking of e.g. static methods and final classes using a familiar API. What most people don’t know is that it’s pretty easy to benefit from PowerMock even for frameworks it doesn’t support. JMock is another popular mocking framework that PowerMock currently doesn’t support but this article will demonstrate some examples of JMock and PowerMock.

Mocking final classes

Unlike EasyMock and Mockito, JMock doesn’t support mocking classes out of the box. For this to work you need to have the jmock-legacy jar file in your classpath. But even if you have you cannot mock classes that are final:

public final class FinalClass {

    public String helloWorld() {
        return "Hello world";
    }
}

But here PowerMock can help. What you need to do is to use the PowerMock JUnit runner and to prepare the FinalClass for test:

@RunWith(PowerMockRunner.class)
@PrepareForTest(FinalClass.class)

But unfortunately this is not enough. For PowerMock to work it needs something called a “ProxyFrameworkImpl” that helps the reflection utilities in PowerMock to find an unproxied class type of a proxied class. It also needs an “AnnotationEnabler” that’s used for injecting mocks. This may sound complicated but since JMock is using CGLib to create class proxies you can use PowerMock’s EasyMock implementation of the “ProxyFrameworkImpl” and “AnnotationEnabler”. This works because EasyMock is also using CGLib. Thus you need to put the powermock-api-easymock.jar in your class-path (note that you don’t need the easymock.jar in the classpath).

Once we have the powermock-api-easymock.jar in the classpath and have added the two annotations we’re ready to test the FinalClass. Here’s a full example:

@RunWith(PowerMockRunner.class)
@PrepareForTest(FinalClass.class)
public class JMockFinalClassTest {
    private Mockery context = new JUnit4Mockery(){{
        setImposteriser(ClassImposteriser.INSTANCE);
    }};

    private FinalClass tested;

    @Before
    public void setup() throws Exception {
        tested = context.mock(FinalClass.class);
    }

    @Test
    public void mockFinalClassWithPowerMockAndJMock() throws Exception {
        // Given
        final String expected = "something";
        context.checking(new Expectations(){{
            one(tested).helloWorld();
            will(returnValue(expected));
        }});

        // When
        final String actual = tested.helloWorld();

        // Then
        assertEquals(expected, actual);
        context.assertIsSatisfied();
    }
}

Mocking Stubbing static methods

Now that we know how to mock final classes, how about static methods? The bad news is that you can’t mock them. But the good news on the other hand is that PowerMock provides a MemberModification API that allows you to stub, suppress and replace methods. So to stub a static method regardless of the underlying mock framework you can do:

stub(method(ClassWithStaticMethod.class, "staticMethodName")).toReturn(someObject);

To demonstrate how this can be used with JMock imagine that we want to test the generateMessage method in this example:

public class JMockExample {
    private final FinalClass finalClass;

    public JMockExample(FinalClass finalClass) {
        this.finalClass = finalClass;
    }

    public String generateMessage() {
        return "Message is: "+ finalClass.helloWorld() + ClassWithStaticMethod.returnString();
    }
}

where FinalClass is the same as in the previous example and ClassWithStaticMethod looks like this:

public class ClassWithStaticMethod {
    public static String returnString() {
        return "a string";
    }
}

A full JMock test with PowerMock can look like this:

@RunWith(PowerMockRunner.class)
@PrepareForTest({FinalClass.class, ClassWithStaticMethod.class})
public class JMockStaticMethodTest {
    private Mockery context = new JUnit4Mockery(){{
        setImposteriser(ClassImposteriser.INSTANCE);
    }};

    private FinalClass finalClassMock;
    private JMockExample tested;

    @Before
    public void setup() throws Exception {
        finalClassMock = context.mock(FinalClass.class);
        tested = new JMockExample(finalClassMock);
    }

    @Test
    public void stubbingAndMockingWithPowerMockAndJMock() throws Exception {
        // Given
        context.checking(new Expectations(){{
            one(finalClassMock).helloWorld();
            will(returnValue("Hello "));
        }});

        // Stub the static method
        stub(method(ClassWithStaticMethod.class, "returnString")).toReturn("JMock");

        // When
        final String message = tested.generateMessage();

        // Then
        assertEquals("Message is: Hello JMock", message);
        context.assertIsSatisfied();
    }
}

You can check out the examples from our SVN repo.

Notes

JMock also provides its own JUnit runner called “JMock” that automatically calls context.assertIsSatisfied() after each test. Normally you can use PowerMock with other JUnit runners using the PowerMockRule but unfortunatley the JMock runner extends from a deprecated runner which doesn’t support rules.

Conclusion

As you can see it’s pretty simple to use some PowerMock basics even for unsupported frameworks as JMock. Of course it’s possible to create full-blown support for JMock using PowerMock to enable e.g. new instance and static method mocking but until that day it’s good to know that alternatives exist. If you’re interested to know more about PowerMock please visit our webpage.

This Post Has 7 Comments

  1. Steve Freeman

    Small point, there’s an @Rule-based runner in the jMock source code. We just haven’t packaged it up yet.

  2. Sandeep

    Thanks for sharing the information!!

  3. francis.xjl

    thx for sharing. I really need this information.
    And I really hope powermock will support jmock in the future.

  4. Tim Wood

    Thanks, its a shame the JMock website is not updated with this information, the current suggestion of using JDave is not as good.

  5. kishore

    This works like charm.

Leave a Reply