This blog is now hosted at www.richashworth.com. Please update your bookmarks accordingly.
Lines of Code
Monday, August 27, 2012
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:
which has a dependency on a DataProvider:
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:
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/
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/
Location:
London, UK
Friday, December 30, 2011
Using Reflection To Create Mock Objects
Well-established tools like Mockito and EasyMock offer a powerful range of features for defining and interacting with mock objects. I use both tools heavily in my unit tests, and would recommend them to anyone looking for a mocking framework.
Sometimes, however, a real (i.e. non-proxied) collaborator is called for, or adding third-party libraries may not be an option. In most cases, a suitable object could be instantiated by calling the constructor and setting it as a dependency to the object under test. Suppose though that we have the following 'Footballer' class, and we want to write a test for the 'getAvgGoalsPerGame()' method:
We know that unless both of the 'gamesPlayed' and 'goalsScored' are initialised, a NullPointerException will be thrown, so we might initially write the test as follows:
This is all well and good, but when the number of fields that need to be set increases, the unit tests quickly become littered with code required to set up the mocks. In this case, it makes sense to perform the initialisation outside the test class.
--edit--
An alternative solution avoiding Reflection would be to create Builders for the classes to be mocked. An excellent article on the approach can be found here.
---------
A generic MockObjectPopulator is therefore required, and an outline of what this might look like is given in the example below:
Sometimes, however, a real (i.e. non-proxied) collaborator is called for, or adding third-party libraries may not be an option. In most cases, a suitable object could be instantiated by calling the constructor and setting it as a dependency to the object under test. Suppose though that we have the following 'Footballer' class, and we want to write a test for the 'getAvgGoalsPerGame()' method:
package com.rich.testingutils.mocking; public class Footballer { private String name; private int age; private Double salary; private Integer gamesPlayed; private Integer goalsScored; private Boolean isCaptain; public double getAvgGoalsPerGame(){ return (double) goalsScored / gamesPlayed; } // <getters and setters> // <hashcode> // <equals> }
We know that unless both of the 'gamesPlayed' and 'goalsScored' are initialised, a NullPointerException will be thrown, so we might initially write the test as follows:
package com.rich.testingutils.mocking; import org.junit.Test; import static junit.framework.Assert.assertEquals; public class FootballerTest { @Test public void testGetGameGoalsRatio() { Footballer footballer = new Footballer(); footballer.setGamesPlayed(14); footballer.setGoalsScored(7); assertEquals(Double.valueOf(0.5), footballer.getAvgGoalsPerGame()); } }
This is all well and good, but when the number of fields that need to be set increases, the unit tests quickly become littered with code required to set up the mocks. In this case, it makes sense to perform the initialisation outside the test class.
--edit--
---------
A generic MockObjectPopulator is therefore required, and an outline of what this might look like is given in the example below:
package com.rich.testingutils.mocking; import java.lang.reflect.Method; import java.util.Map; import static org.junit.Assert.fail; public class MockObjectPopulator { private DefaultValues defaultValues = new DefaultValues(); public void populateMockObject(Object obj) { populateMockObjectWithCustomValues(obj, null); } public void populateMockObjectWithCustomValues(Object obj, Map<String, Object> fieldMap) { try { for (Method f : obj.getClass().getDeclaredMethods()) { String methodName = f.getName(); if (methodName.startsWith("set")) { Class type = f.getParameterTypes()[0]; Object value; String fieldName = methodName.replace("set", ""); fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); if (fieldMap != null && fieldMap.containsKey(fieldName)) { value = fieldMap.get(fieldName); } else { value = getDefaultValueByType(type); } if (value != null) { f.invoke(obj, value); } } } } catch (Exception e) { e.printStackTrace(); fail(); } } private Object getDefaultValueByType(Class aClass) throws NoSuchMethodException { String typeName = aClass.getName(); Map<String, Object> defaultValuesMap = defaultValues.getDefaultValuesMap(); Object object = defaultValuesMap.get(typeName); return object; } }
In the above implementation, a map is constructed to associate the field names with their values, and this is then applied to the object passed as a parameter. Where the desired value for a field is not explicitly given in the map, a value is retrieved from the 'DefaultValues' class based on the declared type. An implementation of the DefaultValues class is given below. Note that it can be loaded with values either explicitly or through dependency injection.
package com.rich.testingutils.mocking; import java.util.HashMap; import java.util.Map; public class DefaultValues { private Map<String, Object> defaultValuesMap; public DefaultValues() { this.defaultValuesMap = new HashMap<String, Object>(); initMapWithPresets(); } public DefaultValues(Map<String, Object> defaultValuesMap) { this.defaultValuesMap = defaultValuesMap; initMapWithPresets(); } private void initMapWithPresets() { if (defaultValuesMap == null) { defaultValuesMap = new HashMap<String, Object>(); } for(PresetDefaultValue pdv : PresetDefaultValue.values()){ defaultValuesMap.put(pdv.typeName, pdv.defaultValue); } } public Map<String, Object> getDefaultValuesMap() { return defaultValuesMap; } public void setDefaultValuesMap(Map<String, Object> defaultValuesMap) { this.defaultValuesMap = defaultValuesMap; } private enum PresetDefaultValue { DOUBLE(Double.class.getName(), 0.0), STRING(String.class.getName(), ""), FLOAT(Float.class.getName(), Float.MIN_VALUE), LONG(Long.class.getName(), Long.MIN_VALUE), SHORT(Short.class.getName(), Short.MIN_VALUE), INTEGER(Integer.class.getName(), 0), BOOLEAN(Boolean.class.getName(), false), BYTE(Byte.class.getName(), Byte.MIN_VALUE), CHARACTER(Character.class.getName(), ' '); private String typeName; private Object defaultValue; private PresetDefaultValue(String typeName, Object defaultValue) { this.typeName = typeName; this.defaultValue = defaultValue; } } }Returning to our Footballer example, we can now write the unit test as follows:
package com.rich.testingutils.mocking; import org.junit.Test; import java.util.HashMap; import java.util.Map; import static junit.framework.Assert.assertEquals; public class FootballerTest { private MockObjectPopulator mockObjectPopulator = new MockObjectPopulator(); @Test public void testGetGameGoalsRatio() { Footballer footballer = new Footballer(); Map<String,Object> customValuesMap = new HashMap<String, Object>(); customValuesMap.put("gamesPlayed", 14); customValuesMap.put("goalsScored", 7); mockObjectPopulator.populateMockObjectWithCustomValues(footballer, customValuesMap); assertEquals(Double.valueOf(0.5), footballer.getAvgGoalsPerGame()); } }
All of the code in this example is now on GitHub, so please feel free to reuse anything that you have found useful.
Subscribe to:
Posts (Atom)