Sunday, August 29, 2010

How to mock/stub void static methods using PowerMock

In my previous post we saw how we could mock/stub void instance methods.
In this post we will try our hands at mocking/stubbing another exotic species called void static methods

Yes, I know void static methods should be avoided at all costs!

But sometimes you just cant help it!

So lets look at the code under test

In our example we want to test the upgradeAllCustomers method of the CustomerController.

This method upgrades all customers by invoking a void static method upgradeAll on Customer class. Currently implementation of upgradeAll method of Customer class throws and exception.  

What do we want to test:
  • upgradeAll void static method of Customer class was invoked
  • true is returned after the upgrade
How will we test it - How do they do it!:

The syntax that we had seen in the earlier post will now work anymore

If we notice persist is an instance method on the Customer class (and the above syntax works for instance methods only).

But in this case we want to stub the void static method. How do we do it?

We have to do the following things

  • Use the annotation @RunWith(PowerMockRunner.class) to bootstrap PowerMock itself
  • Use the annotation @PrepareForTest(Customer.class) to be able to stub static methods of Customer class.
  • Stub the void static method using the following lines of code

Notice that the syntax is a bit like record-playback-verify style.

We have to first tell PowerMock to do nothing when a static method is invoked on Customer class.

PowerMock should do nothing on which void static method is specified in the next line by invoking the void static upgradeAll method on the Customer class.

Its effectively recording the fact that, when static void upgradeAll method is invoked on Customer class nothing is to be done!

Before moving, lets see how can we verify that a void static method was actually invoked?

We can easily see that the syntax to verify that a static method was invoked also follows the record-playback-verify pattern.

First we have to inform PowerMock that we are going to verify a static method invocation, and in the next line we actually verify the call to the upgradeAll method.

Enough explanation lets look at the complete test code:

Yes thats how we do it!

For the first time I have to say, and believe me I hate to do so but, I don't like this syntax that much!

But that's just me and hats of to the excellent job that PowerMock and Mockito guys have done so far!

7 comments:

  1. This is probably a basic question but say my static void method cannot be called normally (e.g. I have copied the class under test to a new project to keep the unit tests out of the main project, but I haven't copied everything it needs). I tried mockStatic and spy and I have @PrepareForTest at the top, but it still calls the actual function when I try to stub it. Is this expected behavior? Can you not stub static functions without actually calling them?

    ReplyDelete
  2. Hello Vazor,

    I didn't quiet understand your question. What do you exactly mean when you said "(e.g. I have copied the class under test to a new project to keep the unit tests out of the main project, but I haven't copied everything it needs)"

    But to answer your last question. You can stub static functions without calling them.

    Heres a simple example:

    public class ServiceA {

    public static void doSomething() {
    throw new RuntimeException();
    }
    }

    public class ServiceB {

    public void doSomething() {
    ServiceA.doSomething();
    }
    }

    @RunWith(PowerMockRunner.class)
    @PrepareForTest(ServiceA.class)
    public class ServiceBTest {

    @Test
    public void shouldTestTheStaticMethodInServiceA() {
    mockStatic(ServiceA.class);

    new ServiceB().doSomething();

    verifyStatic();
    ServiceA.doSomething();
    }
    }

    Notice that We are simply doing mockStatic(ServiceA.class). We have not called the static method from the test code. Now if you run the test it passes and the exception is not thrown.

    Does this answer your question?

    ReplyDelete
  3. Yes thank you for the example. It is very clear and straightforward. I would name the function shouldTestTheStaticMethodInServiceB or shouldStubTheStaticMethodInServiceA but that is a minor issue. I am still having a problem though. From what I am reading it seems to be a problem with my runner. I think that if the runner isn't working, then the call to ServiceB.doSomething() still calls the real function in ServiceA. I thought that perhaps I didn't need @RunWith since it worked without it. But when I try it in your example it gives me various initializationError0 errors, depending on whether I have junit-4.8.1.jar on the classpath. I will search around for help on these errors now. Thanks!

    java.lang.SecurityException: class "org.junit.internal.runners.TestClass"'s signer information does not match signer information of other classes in the same package
    at java.lang.ClassLoader.checkCerts

    or

    java.lang.NoClassDefFoundError: org/junit/internal/runners/TestClass
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.(PowerMockJUnit44RunnerDelegateImpl.java:83)

    ReplyDelete
  4. A quick search of the first error revealed the final solution I needed.

    http://code.google.com/p/powermock/wiki/FAQ

    The first FAQ details the solution that worked for me:

    Try switching from the org.powermock.modules.junit4.PowerMockRunner to org.powermock.modules.junit4.legacy.PowerMockRunner or vice versa.

    And now your example works, and I can finally start writing tests! Thank you so much!

    ReplyDelete
  5. Hello Vazor,

    I am glad that it worked out. About the method name, yes, I would agree with you on renaming the method to what you have suggested.

    Thanks

    Deep Shah

    ReplyDelete
  6. Hi Deep,

    Is there any way to mock static void method

    I have one class called Utils

    having a two methods getSession will return Session object
    and second method closeSession is the void method

    How to mock this.

    ReplyDelete
  7. Sorry to add ...I want to do it with either EasyMock or PowerMock but don't want to use PowerMockito

    ReplyDelete

Have some Fun!