Mock Static Methods with Mockito

Published
In the pursuit of clean object-oriented code, the need to mock static methods may suggest design flaws or code smells, prompting consideration for refactoring. Nevertheless, there are situations where mocking static methods remains crucial despite refactoring efforts.
Over the years, different approaches have been adopted for mocking static methods. Let's delve into these approaches in detail.

Utilizing PowerMockito with Mockito < v3.4.0

Prior to Mockito v3.4.0, PowerMockito was utilized to mock static methods and was compatible with JUnit 4 which you can integrate by adding the following dependency to the pom.xml file.
pom.xml
<properties> <powermock.version>2.0.9</powermock.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>${powermock.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito2</artifactId> <version>${powermock.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>3.3.3</version> <scope>test</scope> </dependency> </dependencies>
In AEM projects, there's often a need to interact with third-party APIs. Let's use Apache HttpClient, commonly used for invoking external APIs, as an example to write unit test using PowerMockito.
RestClientServiceImpl.java
public HttpClient getHttpClient(int... timeout) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); int connectionTimeout = Arrays.stream(timeout).findFirst() .orElse(CONNECTION_TIMEOUT); RequestConfig config = RequestConfig.custom() .setConnectTimeout(connectionTimeout * 1000) .setConnectionRequestTimeout(connectionTimeout * 1000) .setSocketTimeout(connectionTimeout * 1000).build(); httpClientBuilder.setDefaultRequestConfig(config); return httpClientBuilder.build(); }
RestClientServieimplTest.java
@RunWith(PowerMockRunner.class) @PrepareForTest({ HttpClientBuilder.class }) public class RestClientServiceImplTest { @Mock CloseableHttpClient httpClient; @Mock HttpClientBuilder httpClientBuilder; @InjectMocks RestClientServiceImpl restClient; @Before public void setup() { MockitoAnnotations.initMocks(this); PowerMockito.mockStatic(HttpClientBuilder.class); Mockito.when(HttpClientBuilder.create()).thenReturn(httpClientBuilder); Mockito.when(httpClientBuilder.build()).thenReturn(httpClient); } @Test public void getHttpClientTest() { Assert.assertNotNull(restClient); HttpClient httpClient = restClient.getHttpClient(); Assert.assertNotNull(httpClient); } }

Utilizing Mockito-Inline with Mockito < v5.0.0

Prior to Mockito v5.0.0, Mockito-Inline was utilized to mock static methods and was compatible with JUnit 5 which you can integrate by adding the following dependency to the pom.xml file.
pom.xml
<properties> <junit5.version>5.4.0</junit5.version> <mockito.version>3.4.0</mockito.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.junit</groupId> <artifactId>junit-bom</artifactId> <version>${junit5.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>${mockito.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> <version>${mockito.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-inline</artifactId> <version>${mockito.version}</version> <scope>test</scope> </dependency> </dependencies>
Code coverage for the same RestClientService from previous example can be achieved using Mockito-Inline as shown below.
RestClientServieimplTest.java
@ExtendWith(MockitoExtension.class) public class RestClientServiceImplTest { @Mock CloseableHttpClient httpClient; @Mock HttpClientBuilder httpClientBuilder; @InjectMocks RestClientServiceImpl restClient; @BeforeEach public void setup() { MockitoAnnotations.initMocks(this); } @Test public void getHttpClientTest() { try(MockedStatic<HttpClientBuilder> mocked = Mockito.mockStatic(HttpClientBuilder.class)) { httpClientMockedStatic.when(HttpClientBuilder::create).thenReturn(httpClientBuilder); Mockito.when(httpClientBuilder.build()).thenReturn(httpClient); Assertions.assertNotNull(restClient); HttpClient httpClient = restClient.getHttpClient(); Assertions.assertNotNull(httpClient); } } }
From Mockito v5.0.0 onwards, mockito-inline is no longer required. The same code coverage implementation works as expected, similar to how it works with mockito-inline.
Write your Comment