Mock static method

Foreword

If you already read some other blog post about unusual mocking, you can skip prelude via this link.

I was asked to put together examples how to mock Java constructs well know for their testability issues:

I am calling these techniques unusual mocking. I was worried that such examples without any guidance can be widely used by teammates not deeply experienced in mocking frameworks.

Developers practicing TDD or BDD should be aware of testability problems behind these constructs and try to avoid them when designing their tests and modules. That is the reason why you probably wouldn’t be facing such unusual mocking often on project using these great programming methodologies.

But sometimes you have to extend or maintain legacy codebase that usually contains low cohesive classes. In most cases there isn’t time in current hectic agile world to make such class easy to unit test standard way. When you are trying to unit test such class you often realize that unusual mocking is needed.

That is why I decided to create and share refactoring considerations alongside with examples and workarounds for unusual mocking. Examples are using Mockito and PowerMock mocking frameworks and TestNG unit testing framework.

Mock static method

Refactoring considerations

  1. No mocking – In theory, static methods should be used only in small utility classes. Their functionality should be simple enough. So there shouldn’t be need to  mock static method.
  2. Converting into Spring/EJB bean – If the functionality in static method isn’t simple enough and mocking is needed, consider converting class into Spring/EJB singleton bean. Such bean can be injected into testing class. This is easily mockable by plain Mockito functionality (see this blog post).

Workaround using Mockito

This is my preferred technique when I need to mock static method. I believe that minor exposing of internal implementation in flavor to enhance testability of testing module is much lower risk for project than fall into bytecode manipulation mocking  framework like PowerMock or JMockIt.

This technique involves:

  • Encapsulation of static methods into default method
  • Partial mock (spy) is used to mock this method during testing

Mockito example covers:

  1. Mocking of encapsulated method with return value
  2. Mocking of encapsulated void method
  3. Verifying of encapsulated method calls

Class under test:

public class HumanityMockito {
	/**
	 * Is used as testing target to demostrate static mocking workaround
	 * 
	 * @param greenHouseGasesList
	 *            list of greenhouse gases amounts to release into atmosphere
	 * @return greenhouse gases levels in atmosphere
	 */
	public double revageAthmoshere(Collection<Integer> greenHouseGasesList) {
		for (int greenhouseGases : greenHouseGasesList) {
			releaseGreenHouseGases(greenhouseGases);
		}
		return monitorRevageOfAthmosphere();
	}

	/**
	 * Void method with default access modifier to be mocked. Wraps static
	 * method call.
	 * 
	 * @param volume
	 *            volume of greenhouse gases to release
	 */
	void releaseGreenHouseGases(int volume) {
		Athmosphere.releaseGreenhouseGases(volume);
	}

	/**
	 * Method with return value and default access modifier to be mocked. Wraps
	 * static method call.
	 * 
	 * @return greenhouse gases level
	 */
	double monitorRevageOfAthmosphere() {
		return Athmosphere.getGreenhouseGassesLevel();
	}
}

Test:

public class HumanityMockitoTest {
	private static final int BILION_TONS_CO2 = 5;
	private static final double GREENGOUSE_GASSES_LEVEL = 393.1;

	@Test
	public void testRevageAthmoshere() {
		HumanityMockito humanity = new HumanityMockito();
		HumanityMockito humanitySpy = Mockito.spy(humanity);

		Mockito.doReturn(GREENGOUSE_GASSES_LEVEL).when(humanitySpy)
				.monitorRevageOfAthmosphere();
		Mockito.doNothing().when(humanitySpy)
				.releaseGreenHouseGases(BILION_TONS_CO2);

		// invoke testing method
		Collection<Integer> greenHouseGassesList = new ArrayList<>(
				Arrays.asList(BILION_TONS_CO2, BILION_TONS_CO2));
		double actualLevel = humanitySpy.revageAthmoshere(greenHouseGassesList);

		Assert.assertEquals(actualLevel, GREENGOUSE_GASSES_LEVEL);
		Mockito.verify(humanitySpy, Mockito.times(greenHouseGassesList.size()))
				.releaseGreenHouseGases(BILION_TONS_CO2);
	}
}

Usage of PowerMock

Before usage of this example, please carefully consider if it is worth to bring bytecode  manipulation risks into your project. They are gathered in this blog post. In my opinion it should be used only in very rare and non-avoidable cases.

Test shows how to mock static method by PowerMock directly. Example covers:

  1. Mocking of static method with return value
  2. Mocking of static void method
  3. Verifying of static method calls

Class under test:

public class HumanityPowerMock {
	public double revageAthmoshere(Collection<Integer> greenHouseGasesList) {
		for (int greenhouseGases : greenHouseGasesList) {
			Athmosphere.releaseGreenhouseGases(greenhouseGases);
		}
		return Athmosphere.getGreenhouseGassesLevel();
	}
}

Test:

@PrepareForTest(Athmosphere.class)
public class HumanityPowerMockTest extends PowerMockTestCase {
	private static final int BILION_TONS_CO2 = 5;
	private static final double GREENGOUSE_GASSES_LEVEL = 393.1;

	@Test
	public void testRevageAthmoshere() {
		PowerMockito.mockStatic(Athmosphere.class);

		Mockito.when(Athmosphere.getGreenhouseGassesLevel()).thenReturn(
				GREENGOUSE_GASSES_LEVEL);

		// call of static method is required to mock it
		PowerMockito.doNothing().when(Athmosphere.class);
		Athmosphere.releaseGreenhouseGases(BILION_TONS_CO2);

		// invoke testing method
		Collection<Integer> greenHouseGassesList = new ArrayList<>(
				Arrays.asList(BILION_TONS_CO2, BILION_TONS_CO2));
		HumanityPowerMock humanity = new HumanityPowerMock();
		double actualLevel = humanity.revageAthmoshere(greenHouseGassesList);

		Assert.assertEquals(actualLevel, GREENGOUSE_GASSES_LEVEL);

		// call of static method is required to verify it
		PowerMockito.verifyStatic(Mockito.times(greenHouseGassesList.size()));
		Athmosphere.releaseGreenhouseGases(BILION_TONS_CO2);
	}
}

Links

Source code can be downloaded from Github.

Other unusual mocking examples:

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>