by Johan Haleby - Java, Testing, Tips & Tricks
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.
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:
|
1 2 3 4 5 6 |
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:
|
1 2 |
@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:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
@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(); } } |
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:
|
1 |
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:
|
1 2 3 4 5 6 7 8 9 10 11 |
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:
|
1 2 3 4 5 |
public class ClassWithStaticMethod { public static String returnString() { return "a string"; } } |
A full JMock test with PowerMock can look like this:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
@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.
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.
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.
Thanks, its a shame the JMock website is not updated with this information, the current suggestion of using JDave is not as good.
thx for sharing. I really need this information.
And I really hope powermock will support jmock in the future.
Thanks for sharing the information!!
[...] This post was mentioned on Twitter by jayway, Kissenlall Rambojun. Kissenlall Rambojun said: Reading: "http://www.dzone.com/links/r/powermock_with_unsupported_frameworks_such_as_jmo.html"( http://twitthis.com/845tz2 ) [...]
Ah that’s nice, thanks for pointing out.
Small point, there’s an @Rule-based runner in the jMock source code. We just haven’t packaged it up yet.