Friday, March 16, 2012

Turbocharge your mocking framework with PowerMock


As mentioned in my last post, I am an ardent fan of the Mockito Framework. Originally developed as a fork of EasyMock, it equips developers with the tools to create mock objects using clean and expressive code. In this post, I would like to illustrate how PowerMock can be used alongside Mockito (or EasyMock) to create mocks for classes that appear at first 'unmockable'. Suppose we have the following class:

package org.rich.powermockexample;

import java.util.List;

public class ResultsWriter {
    
    public String getDataAsString(){
        StringBuilder builder = new StringBuilder();
        List<String> dataList = DataProvider.getData();
        for(String str: dataList){
            builder.append(str + "\n");
        }
        return builder.toString();
    }

}

which has a dependency on a DataProvider:

package org.rich.powermockexample;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;

import static com.google.common.io.Files.readLines;

public final class DataProvider {

    public static List<String> getData() {
        List<String> data = null;
        try {
            data = new DataProvider().readFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return data;
    }

    private static List<String> readFile() throws IOException {
        File file = new File("/path/to/file");
        List<String> lines = readLines(file, Charset.forName("utf-8"));
        return lines;
    }

}

If we are to write a unit test for the ResultsWriter class, then we will need to mock the DataProvider, to avoid depending on a file in the filesystem. However, it is not always viable to refactor production code to make it more testable, and since the DataProvider class is final, we cannot directy mock it with Mockito or EasyMock.

This is where PowerMock comes into play. With features to mock (or partially mock) final classes, static methods, and private methods, we have the tools at our disposal to test whatever the codebase may throw at us. A unit test for the ResultsWriter class, using a partial mock of the DataProvider is given below:

package org.rich.powermockexample;

import org.junit.Before;
import org.junit.BeforeClass;
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 java.util.ArrayList;
import java.util.List;

import static org.junit.Assert.assertEquals;


@RunWith(PowerMockRunner.class)
@PrepareForTest(DataProvider.class)
public class ResultsWriterTest {

    private static List<String> mockData = new ArrayList<String>();
    private ResultsWriter resultsWriter;

    @BeforeClass
    public static void setUpOnce() {
        final String firstLine = "Line 1";
        final String secondLine = "Line 2";
        mockData.add(firstLine);
        mockData.add(secondLine);
    }

    @Before
    public void setUp() {
        resultsWriter = new ResultsWriter();
    }

    @Test
    public void testGetDataAsString() throws Exception {
        PowerMockito.spy(DataProvider.class);
        PowerMockito.doReturn(mockData).when(DataProvider.class, "readFile");

        final String expectedData = "Line 1\nLine 2\n";
        final String returnedString = resultsWriter.getDataAsString();

        assertEquals(expectedData, returnedString);
    }

}

In the above example, we have barely scratched the surface of what PowerMock is capable of. Explore more ways to turbocharge your favourite mocking framework at http://code.google.com/p/powermock/

No comments:

Post a Comment