Date and Time Testing

Working with dates and times can be challenging for developers, especially regarding testing. When testing code that involves the current date or time, it takes time to ensure that the results are correct and consistent. Luckily, there are several ways to solve this problem.

One approach is to use a fixed date and time in your tests. This ensures that your code produces consistent results, regardless of the actual date and time. You can achieve this by mocking the now() method of the LocalDateTime class, which returns the current date and time.

Here's an example of how to mock the now() method of the LocalDateTime class using Mockito Inline and Java:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <scope>test</scope>
</dependency>
try (MockedStatic<LocalDateTime> mockedStatic = Mockito.mockStatic(LocalDateTime.class)) {
    mockedStatic.when(LocalDateTime::now).thenReturn(fixedDate);

    // Your code here.
}

In this code snippet, fixedDate is a LocalDateTime object representing the fixed date and time you want to use in your tests. The MockedStatic class is a Mockito class that allows you to mock static methods.

To make this code more reusable, you can create a small method that accepts a fixedDate and a test in a Runnable. This will help to improve your code significantly and make it more readable:

private void tryOn(LocalDateTime fixedDate, Runnable test) {
    try (MockedStatic<LocalDateTime> mockedStatic = Mockito.mockStatic(LocalDateTime.class)) {
        mockedStatic.when(LocalDateTime::now).thenReturn(fixedDate);
        test.run();
    }
}

You can then use this method in your tests to ensure that your code produces consistent results:

tryOn(fixedDate, () -> {
    // Your code here.
});

Using a fixed date and time in your tests ensures that your code produces consistent results, regardless of the actual date and time. This can help you to identify and fix bugs more quickly and ensure that your code works as expected in all scenarios.

In summary, when testing code that involves the current date and time, it's essential to use a fixed date and time to ensure consistent results. You can achieve this by mocking the now() method of the LocalDateTime class using Mockito and Java. By using a small method like tryOn, you can make your code more reusable and easier to read.

A full code example

import static org.assertj.core.api.Assertions.assertThat;

import java.time.LocalDate;

import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

class AgeCalculatorTest {

    private static final LocalDate APRIL_27_2023 = LocalDate.of(2023, 4, 27);
    
    @Test
    void testCalculateAgeWorksOnlyIn2023() {
        // Arrange
        LocalDate birthDate = LocalDate.of(1993, 4, 27);

        // Act
        int actualAge = AgeCalculator.calculateAge(birthDate);

        // Assert
        int expectedAge = 30;
        assertThat(actualAge).isEqualTo(expectedAge);
    }

    @Test
    void testCalculateAgeWorksEveryYear() {
        // Arrange
        final LocalDate birthDate = LocalDate.of(1993, 4, 27);

        try (MockedStatic<LocalDate> mockedStatic = Mockito.mockStatic(LocalDate.class)) {
            mockedStatic.when(LocalDate::now).thenReturn(APRIL_27_2023);

            // Act
            int actualAge = AgeCalculator.calculateAge(birthDate);

            // Assert
            int expectedAge = 30;
            assertThat(actualAge).isEqualTo(expectedAge);
        }
    }

    @Test
    void testCalculateAgeWorksEveryYearUsingTryOn() {
        // Arrange
        final LocalDate birthDate = LocalDate.of(1993, 4, 27);

        tryOn(APRIL_27_2023, () -> {
            // Act
            int actualAge = AgeCalculator.calculateAge(birthDate);

            // Assert
            int expectedAge = 30;
            assertThat(actualAge).isEqualTo(expectedAge);
        });
    }

    private void tryOn(LocalDate fixedDate, Runnable test) {
        try (MockedStatic<LocalDate> mockedStatic = Mockito.mockStatic(LocalDate.class)) {
            mockedStatic.when(LocalDate::now).thenReturn(fixedDate);

            test.run();
        }
    }

    static class AgeCalculator {

        public static int calculateAge(LocalDate birthDate) {
            LocalDate currentDate = LocalDate.now();
            int age = currentDate.getYear() - birthDate.getYear();
            if (birthDate.getDayOfYear() > currentDate.getDayOfYear()) {
                age--;
            }
            return age;
        }
    }
}