How to mock static methods

mock static decorationAutomated unit testing, according to the most popular definition, assumes testing one object (e.g. a class in Java) at the time. Because of interactions between objects, doing so in isolation from other units is a challenge in most cases. Mockito comes handy in this area. Unfortunately, it does not cover all cases. Static util methods are problematic. Mock in terms of Mockito are objects and as such they cannot be used for mocking static methods.

Luckily, Java community is big enough to not allow this problem stay long without an easy solution.

 

Sample code to be tested

To illustrate the problem and a solution, I have created a small piece of code that calculates surface area of a set of triangles.

Triangle is a basic class defined by a side length and a height:

public class Triangle {
private double side;
private double height;

public Triangle(double side, double height) {
this.side = side;
this.height = height;
}

public double getSide() {
return side;
}

public void setSide(double side) {
this.side = side;
}

public double getHeight() {
return height;
}

public void setHeight(double height) {
this.height = height;
}
}

GeometricFigures is a set of triangles:

import java.util.HashSet;
import java.util.Set;

public class GeometricFigures {
private Set<Triangle> triangleSet = new HashSet<>();

public void addTriangle(Triangle triangle) {
triangleSet.add(triangle);
}

public double getTotalArea() {
double totalArea = 0;

for (Triangle triangle : triangleSet) {
totalArea += MathUtils.calcTriangleArea(triangle.getSide(), triangle.getHeight());
}

return totalArea;
}
}

The getTotalArea method is the one I am going to unit test. As you can see above, this method uses a static method MathUtils.calcTriangleArea to calculate surface area of a triangle based on a side length and a height. As my test is going to be a unit test I want it to be independent on MathUtils. To achieve that, I should mock it, but calcTriangleArea is a static method.

Do not miss valuable content. You will receive a monthly summary email. You can unsubscribe anytime.

Mocking static method with PowerMock

This is a moment when PowerMock comes on the scene. To be able to use PowerMock, it has to be included in the project in the first place. I use Gradle to manage Java packages, so just add PowerMock dependency in my main build.gradle file.

dependencies {
testCompile group: 'junit', name: 'junit', version: '4.11'
testCompile group: 'org.powermock', name: 'powermock-mockito-release-full', version: '1.6.4'
}

Then, a unit test can be written with a mocked static method.

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

import static org.junit.Assert.*;
import static org.mockito.BDDMockito.given;
import static org.powermock.api.mockito.PowerMockito.mockStatic;

@RunWith(PowerMockRunner.class)
@PrepareForTest(MathUtils.class)
public class GeometricFiguresTest {
private GeometricFigures geometricFigures;

@Before
public void setUp() throws Exception {
geometricFigures = new GeometricFigures();
}

@Test
public void testGetTotalArea() throws Exception {
// given
mockStatic(MathUtils.class);
given(MathUtils.calcTriangleArea(2, 5)).willReturn(5d);

geometricFigures.addTriangle(new Triangle(2, 5));

// then
double totalArea = geometricFigures.getTotalArea();

// then
assertEquals(5, totalArea, 0.1);
}
}

It consists of a few necessary elements to make it all working:

  1. The default JUnit test runner has to be replaced with the PowerMock runner:
    @RunWith(PowerMockRunner.class)
  2. Before mocking a static method, the class which contains it has to be prepared by PowerMock:
    @PrepareForTest(MathUtils.class)
  3. A static method is mocked by the following two lines.
    mockStatic(MathUtils.class);
    given(MathUtils.calcTriangleArea(2, 5)).willReturn(5d);
    It means that the surface area of triangle (2, 5) is assumed 5.

Other elements are not strictly connected to PowerMock but more to Mockito, JUnit or unit testing in general.

If you like what I do, consider buying me a coffee :)

Buy me a coffeeBuy me a coffee