We are the Dev Teams of
  • brands
  • ebay_main
  • ebay
  • mobile
<
>
BLOG

Testing Legacy Code with Mockito and PowerMock

by Peter Laufer
in Tutorials

The life of a software developer isn't always fun. Unfortunately, you don't always get to play around with the latest and greatest technologies on all of your projects, but you do have to deal with a certain amount of legacy code from time to time that needs some adjustments or new functionality.

And OMG there aren't even unit tests around and writing new ones is oh so painful as the classes might be complex, the number of dependencies high and lots of static utility helpers might be involved as well...

But fear not: "There's A Mock For That!"

Unit tests are at the core of high quality software. They are known to be fast, reliable and usually easy to write. The Java eco-system provides a rich set of testing and mocking frameworks. At mobile.de we use JUnit which is by far the most popular testing framework for Java. Additionally we relied on EasyMock quite extensively for mocking dependencies and testing interactions between classes. Impressed by the ease of use and improved test code readability, more and more of our devs have switched to Mockito in recent years. Especially the annotation based test setup which reduces the amount of boiler plate code and makes tests easier to read and maintain.

However, there are a few things just not possible with EasyMock and Mockito alone, among them: mocking private fields and mocking static methods. 

Introducing PowerMock

PowerMock can be regarded as an addon to EasyMock or Mockito that allows you to "unit test code normally regarded as untestable". You basically keep on testing like before, but instead of avoiding testing code that calls static helpers or setting up a lot of test data for the static helpers to work on, you let PowerMock do the mocking of the static methods for you.

Assume you have the following code in one of your service implementations:

public TextModel getMakeModelText(final Ad ad, final Customer customer) {
    if (messages != null) {
        return getMakeModelText(ad);
    } else {
        return MakeModelAssembler.assemble(ad, customer);
    }
}

And now assume that the MakeModelAssembler's assemble method is quite complex and calls some more static methods to get its job done. Apart from the fact that mocking everything to make the assemble method return a proper TextModel is quite hard, you simply don't care about its implementation when you want to unit test your service. Furthermore, if your service unit test relies on a certain implementation of the static method it is likely to break whilst refactoring and will thus come with higher test maintenance costs.

So let's have a look on how to mock the static method with the help of PowerMock:

...
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;

@RunWith(PowerMockRunner.class)
@PrepareForTest(MakeModelAssembler.class)
public class ServiceTest {

    @Test
    public void getMakeModelTextShouldUseMakeModelAssemblerWhenMessagesAreNotAvailable() {
        PowerMockito.mockStatic(MakeModelAssembler.class);
        when(MakeModelAssembler.assemble(any(Ad.class), any(Customer.class))).thenReturn(new TextModel("assembled"));

        TextModel model = service.getMakeModelText(ad, customer);

        assertTrue(model.getValue().contains("assembled"));
    }

}

There's basically only three little things that you have to do to allow PowerMock to work its magic:

  1. Add the @RunWith(PowerMockRunner.class) annotation.
  2. Add the @PrepareForTest(MakeModelAssembler.class) annotation to tell PowerMock which classes you want to mock static methods from.
  3. Call PowerMockito.mockStatic(MakeModelAssembler.class) before specifying your expections using Mockito's when method.

Once you have the setup right, you can use any of Mockito's expection and verification calls just like on your regular mock objects.

If you build your projects with Maven and want to use PowerMock with Mockito and JUnit you have to add the following dependencies to your project's pom.xml:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>1.9.5</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>1.5.3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito</artifactId>
    <version>1.5.3</version>
    <scope>test</scope>
</dependency>

This example shows that there's actually no excuse for not improving quality when working with legacy code. There's basically nothing that you can't mock. So, you can make small changes and cover them with tests without having to invest a lot of time.

 

Testing, Java, Mockito, PowerMock, Legacy Code

?>