Static Methods: Overcoming the Testing Challenges

Priyan Prabhu
3 min readApr 3, 2023

--

Mocking static methods of Java classes (java.time.LocalDateTime, java.time.Instant) can be a challenging task in JUnit 5, but with the right approach, it can be done effectively. In this blog, we will discuss how to mock static methods of Java classes in JUnit 5 using Mockito

In this example let us see a scenario where you might need to mock LocalDateTime or Instant classes in JUnit.

Consider a method where a Date Time is passed as a parameter and it needs to verify the parameter is passed is greater (or lesser depending on use case) then current Date time (LocalDateTime.now()).

......
public boolean bookAppointment(Patient patient, LocalDateTime appointmentDateTime) {

//printing this log to show what the mockito changed the time
log.info("LocalDateTime.now() {}", LocalDateTime.now());
if (appointmentDateTime.isBefore(LocalDateTime.now())) {
throw new IllegalArgumentException("Appointment Datetime cannot be less than today date");
}
// additional business logic
return true;

}
.....

In order to unit test is method, the developer need to pass a LocalDateTime object , which eventually will become before than LocalDateTime.now() and testcases starts failing.

There are different ways in which a developer can overcome this problem, but one of the solution is mocking LocalDateTime.now() which is the objective of the article.

We are going to use Mockito 4.0.0, only from 3.4.0 , Mockito started supporting mocking static methods.. prior only powermocks was the only option to mock static methods.

pom.xml

....
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId> <!-- this is needed for static method testing-->
<version>4.0.0</version>
<scope>test</scope>
</dependency>
....

AppointmentServiceTest.java

package com.prabhu;

import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.time.LocalDate;
import java.time.LocalDateTime;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;

import com.prabhu.patient.AppointmentServices;
import com.prabhu.patient.Patient;

@ExtendWith(MockitoExtension.class)
class AppointmentServiceTest {

@Spy
private AppointmentServices appointmentService;


@Test
void test_appointment_date_in_past() {
Patient testPatient = Patient.builder().firstName("John").lastName("Doe").dateOfBirth(LocalDate.now()).build();

LocalDateTime fixedDateTime = LocalDateTime.of(2023, 4, 2, 10, 00);
try (MockedStatic<LocalDateTime> mockedLocalDateTime = Mockito.mockStatic(LocalDateTime.class)) {
mockedLocalDateTime.when(LocalDateTime::now).thenReturn(fixedDateTime);

appointmentService.bookAppointment(testPatient, fixedDateTime);
}
}

@Test
void test_appointment_date_not_in_past()
{
Patient testPatient = Patient.builder().firstName("John").lastName("Doe").dateOfBirth(LocalDate.now()).build();

Exception exception = assertThrows(IllegalArgumentException.class, () -> {
appointmentService.bookAppointment(testPatient, LocalDateTime.of(2023, 4, 2, 10, 00));

});

assertEquals("Appointment Datetime cannot be less than today date", exception.getMessage());

}

}

Both the first and second unit test methods are using same LocalDateTime value as paramete i.e LocalDateTime.of(2023, 4, 2, 10, 00) but first one passes the test and second one fails throw the exception as it is past date.

The difference being, first unit test utilizes Mockito.mockStatic to mock LocalDateTime class.

The MockedStatic class is part of Mockito (> 3.4.0) and is used to mock static methods. You can use the when method to specify the behavior of the static method when it is called.

When you run the unit test methods, you can see these in the console logs.

The test_appointment_date_not_in_past method , does not have any mock, hence printing the current time

18:11:06.298 [main] INFO com.prabhu.patient.AppointmentServices — LocalDateTime.now() 2023–04–03T18:11:06.293795

The test_appointment_date_in_past method have mocked the LocalDateTime.now() to return 2023–04–02T10:00 hence it prints

18:11:06.894 [main] INFO com.prabhu.patient.AppointmentServices — LocalDateTime.now() 2023–04–02T10:00

P.S — Mocking static methods can be useful in certain scenarios, but it is generally not considered good practice in software testing. But sometimes it becomes unavoidable.. in those situations, you can use the above method.

Source Code — https://github.com/prabhushan/unit-testing

--

--

Priyan Prabhu

Technical Architect, Love to do coding / guiding others on Java and other related languages.