Monday, May 10, 2010

How to mock final methods and classes using PowerMock

This is the second post in the how-to series of using PowerMockito to write beautiful unit-tests. In my previous post we saw how can we write unit-tests for mocking static methods.

Today, I will demostrate how we can mock final class and final methods using PowerMocks (Mockito api) - Mocks on steroids!

Requirement:

I am going to take a ficticious example, were AnotherService class's invokeFinal() method invokes a final method on the instance of FinalService class which is a final class.

Code under test:

The final method doSomething in the final Class FinalService does nothing, just throws an exception.  AnotherService's invokeFinalMethod invokes doSomething method.

Most common mocking framework in the java world cannot mock final classes/methods because they are typically based on creating proxies.  Creating proxies for final classes is not possible as we cannot subclass a final classes.

So how we do we it in PowerMocks (Mockito api)?

How will we test it - How do they do it!:

Aha! Its tad simple.  We don't need to do anything special here.

We have to use @PrepareForTest annotation with FinalService so that PowerMocks can actually mock FinalService (Go Here for explanation on @PrepareForTest).  Next up, we need to create the stub of FinalService using the following code

Then, we have two approaches

I will show both the approaches

State based testing:

Interaction based testing:

Thats it!

It is needless to say that things look pretty easy with PowerMocks (Mockito api).  They look easy because they are easy with PowerMocks!  Yes! you can with PowerMocks (Mockito api)!

Feedback, questions and comments are always welcome.

9 comments:

  1. http://groups.google.com/group/powermock/msg/f5330393ab32d72e - did this work for you?

    ReplyDelete
  2. Hi There,

    Yes, it does work. The example I have posted is an working example.

    The issue you have highlighted is for "verification of instance methods in [final system classes]". The verification of instance methods does work for application final classes.

    ReplyDelete
  3. Hello,

    Is there a way of having more than one mock final implementation in the same test case...

    Like

    @PrepareForTest(Final1.class,Final2.class)

    seems that this is not supported?

    thanks

    ReplyDelete
  4. Hello There,

    The @PrepareForTest takes in array of class types, i.e. you can pass in more than one classes as array argument to PrepareForTest like this

    @PrepareForTest({Final1.class, Final2.class})

    Hope This Helps

    Deep Shah

    ReplyDelete
  5. With PowerMockito 1.4.10 I get (it seems that this doesn't work - I tried the exact same code here):

    java.lang.RuntimeException: Something went wrong
    at test.mockingFinalPowerMockito.FinalService.doSomething(FinalService.java:6)
    at test.mockingFinalPowerMockito.AnotherService.invokeFinalService(AnotherService.java:12)
    at test.mockingFinalPowerMockito.AnotherServiceTest.shouldInvokeDoSomethingMethod(AnotherServiceTest.java:42)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:66)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:307)
    at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:86)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:94)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:294)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:112)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:73)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:282)
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:84)
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:207)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:146)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:118)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:102)
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

    ReplyDelete
  6. That example shouldn't be working since Mockito doesn't stub final methods.
    I'm having the same problem with DefaultHttpClient class in the method execute(...). I had to use the interface HttpClient.

    ReplyDelete
  7. Hello Deksa,

    The example posted here is working. Could you zip your example and send it over to me?

    Hello Héctor Tobón,

    The example is a working example. It uses PowerMock and not just Mockito. Mockito cannot stub final methods but PowerMock can stub final methods.

    Thanks

    Deep Shah

    ReplyDelete
  8. Deska,

    Make sure that you download the PowerMock 1.4.10 with Mockito and JUnit including dependencies

    http://powermock.googlecode.com/files/powermock-mockito-junit-1.4.10.zip

    ReplyDelete
  9. I want to mock the private method I am using the http://powermock.googlecode.com/files/powermock-mockito-junit-1.4.10.zip , will I be able to mock private method with powermock-mockito 1.4.10 ?

    ReplyDelete

Have some Fun!