How can I mock java.time.LocalDate.now()
Asked Answered
A

11

105

In my test case, I need test time sensitive method, in that method we're using java 8 class LocalDate, it is not Joda.

What can I do to change time, when I'm running test

Adrianople answered 25/9, 2015 at 23:26 Comment(0)
H
172

In your code, replace LocalDate.now() with LocalDate.now(clock);.

You can then pass Clock.systemDefaultZone() for production and a fixed clock for testing.


This is an example :

First, inject the Clock. If you are using spring boot just do a :

@Bean
public Clock clock() {
    return Clock.systemDefaultZone();
}

Second, call LocalDate.now(clock) in your code :

@Component
public class SomeClass{

    @Autowired
    private Clock clock;

    public LocalDate someMethod(){
         return LocalDate.now(clock);
    }
}

Now, inside your unit test class :

// Some fixed date to make your tests
private final static LocalDate LOCAL_DATE = LocalDate.of(1989, 01, 13);

// mock your tested class
@InjectMocks
private SomeClass someClass;

//Mock your clock bean
@Mock
private Clock clock;

//field that will contain the fixed clock
private Clock fixedClock;


@Before
public void initMocks() {
    MockitoAnnotations.initMocks(this);

    //tell your tests to return the specified LOCAL_DATE when calling LocalDate.now(clock)
    fixedClock = Clock.fixed(LOCAL_DATE.atStartOfDay(ZoneId.systemDefault()).toInstant(), ZoneId.systemDefault());
    doReturn(fixedClock.instant()).when(clock).instant();
    doReturn(fixedClock.getZone()).when(clock).getZone();
}

@Test
public void testSomeMethod(){
    // call the method to test
    LocalDate returnedLocalDate = someClass.someMethod();

    //assert
    assertEquals(LOCAL_DATE, returnedLocalDate);
}
Horatia answered 26/9, 2015 at 7:27 Comment(6)
Is there a way to mock without changing code(LocalDateTime.now()) and Without using Powermock?Brahmaputra
Is there a reason to define a custom @Bean definition for Clock? I've omitted it using Spring Boot 2.1 and it's working like a charm :-)Fango
@PimHazebroek the @ Bean is used for the production code not for the unit testFarcy
How do I mock this line of code to return 2020-02-27 LocalDate.now().plusDays(Long.parseLong("0")).toString() but when it come to that line it returns todays dateCranky
This is a proof of a bad java-time design.Merely
Please note that as for Mockito 4.4.0 it is already possible to mock static methods. See https://mcmap.net/q/205768/-how-to-mock-a-zoneddatetime-with-mockito-and-junitClaudine
D
11

You can refactor you code to make it test-friendly, for example, replace all invocations of LocalDate.now() with invocation of some method of custom mockable non-static class.

Alternatively, you can use PowerMock's mockStatic.

Debor answered 26/9, 2015 at 9:40 Comment(0)
R
9

We have to mock a static method here. I use following dependency. Remember all our test code has to be in the try block. As soon as we call LocalDate.now() or LocalDate

<!-- https://mvnrepository.com/artifact/org.mockito/mockito-inline -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>3.11.0</version>
    <scope>test</scope>
</dependency>

The code:

 @Test
    void verifykNonLeapYear() {

        LocalDate currentLocalDate = LocalDate.of(2010, 2, 13);
        try (MockedStatic<LocalDate> topDateTimeUtilMock = Mockito.mockStatic(LocalDate.class)) {
            topDateTimeUtilMock.when(() -> LocalDate.now()).thenReturn(currentLocalDate);
            assertThat(TopDateTimeUtil.numberOfDaysInCurrentYear(), is(365));
        }
    }
Rottenstone answered 4/6, 2021 at 21:0 Comment(3)
Downvote because this destroys the rest of LocalDate functionality (e.g. minusDays, parse) Using Mockito to mock Clock.class instead worked, see https://mcmap.net/q/205769/-mock-instant-now-without-using-clock-into-constructor-or-without-clock-objectFellner
you can also mock just @Before one of the test and close it later. #65965896Trefler
@AlexR that's really foul. mockito lets you choose the default response when invoking a non-mocked static method. MockedStatic<LocalDate> topDateTimeUtilMock = Mockito.mockStatic(LocalDate.class, Mockito.CALLS_REAL_METHODS); This will make it so only the methods you specify will be mocked, everything else will work as normalBeamon
L
3

If we need to mock static methods like now() we can use multiple alternatives like PowerMock:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ LocalDateTime.class })
public class LocalDateTimeUnitTest {

    @Test
    public void givenLocalDateTimeMock_whenNow_thenGetFixedLocalDateTime() {
        Clock clock = Clock.fixed(Instant.parse("2014-12-22T10:15:30.00Z"), ZoneId.of("UTC"));
        LocalDateTime dateTime = LocalDateTime.now(clock);
        mockStatic(LocalDateTime.class);
        when(LocalDateTime.now()).thenReturn(dateTime);
        String dateTimeExpected = "2014-12-22T10:15:30";

        LocalDateTime now = LocalDateTime.now();

        assertThat(now).isEqualTo(dateTimeExpected);
    }
}

Or JMockit, indeed with JMockit we can use the MockUp class:

@Test
public void givenLocalDateTimeWithJMock_whenNow_thenGetFixedLocalDateTime() {
    Clock clock = Clock.fixed(Instant.parse("2014-12-21T10:15:30.00Z"), ZoneId.of("UTC"));
    new MockUp<LocalDateTime>() {
        @Mock
        public LocalDateTime now() {
            return LocalDateTime.now(clock);
        }
    };
    String dateTimeExpected = "2014-12-21T10:15:30";

    LocalDateTime now = LocalDateTime.now();

    assertThat(now).isEqualTo(dateTimeExpected);
}

Or the Expectations class:

@Test
public void givenLocalDateTimeWithExpectations_whenNow_thenGetFixedLocalDateTime() {
    Clock clock = Clock.fixed(Instant.parse("2014-12-23T10:15:30.00Z"), ZoneId.of("UTC"));
    LocalDateTime dateTimeExpected = LocalDateTime.now(clock);
    new Expectations(LocalDateTime.class) {
        {
            LocalDateTime.now();
            result = dateTimeExpected;
        }
    };

    LocalDateTime now = LocalDateTime.now();

    assertThat(now).isEqualTo(dateTimeExpected);
}

We can find more examples here.

Another simple alternative is to use the now() method with a fixed Clock instance. Certainly, most of the classes in java.time package have a now() method with a Clock parameter:

@Test
public void givenFixedClock_whenNow_thenGetFixedLocalDateTime() {
    Clock clock = Clock.fixed(Instant.parse("2014-12-22T10:15:30.00Z"), ZoneId.of("UTC"));
    String dateTimeExpected = "2014-12-22T10:15:30";

    LocalDateTime dateTime = LocalDateTime.now(clock);

    assertThat(dateTime).isEqualTo(dateTimeExpected);
}
Limulus answered 27/12, 2018 at 15:1 Comment(10)
I have tried to not create dateTimeExpected in expectation block but assign LocalDateTime.now() directly in Expectations block but then it does not work. Any idea why?Prevalent
@Prevalent To use JMockit with maven is necessary to put the following in the surefire maven plugin: javaagent:${settings.localRepository}/org/jmockit/jmockit/${jmockit.version}/jmockit-${jmockit.version}.jar More details here: jmockit.github.io/tutorial/Introduction.html#mavenLimulus
I tried the powermock alternative and it does not work.Autopilot
@carlospalma Do you get a specific error? What version of PowerMock/Junit and Java are you using? In the sample, I'm using PowerMock 2, Java 8 and Junit4. Look the code here: github.com/eugenp/tutorials/blob/master/core-java-modules/…Limulus
The dependencies for PowerMock 2 sample are here: github.com/eugenp/tutorials/blob/master/core-java-modules/…Limulus
@SHoko "when requires an argument which has to be a method call on a mock". What I ended up doing was mocking the object which produced the dates instead of trying to mock the system time. That way you can create a unit test that always passes.Autopilot
Does it make a difference that you are actually using LocalDateTime.now() versus LocalDate.now() I keep getting the "when() requires an argument which has to be 'a method call on a mock'." The powermock 2 beta does not seem to like LocalDate.now(). Does it matter that LocalDate is final?Thy
@Jolley71717. Did you call mockStatic(LocalDateTime.class) first?. There no difference between LocalDateTime and LocalDate (except for the return value), both are final classes. Which powermock version do you use?Limulus
@SHoko, I did call mockStatic(LocalDate.class) first. I'm using PowerMock version 2.0.0-beta.5Thy
@Thy Could you try a newer version like 2.0.0-RC.4 or 2.0.0?. This samples run fine in that versions.Limulus
B
3

If you are here and using Mockito and Kotlin, do the following:

        mockStatic(LocalDate::class.java, Mockito.CALLS_REAL_METHODS).use {
        `when`(LocalDate.now()).thenReturn(date)
        // Do your stuff here
    }

If you are using Java, check out this issue for how it is done.

Boxhaul answered 9/2, 2023 at 10:52 Comment(0)
S
2

You can use supplier inside your class which you are testing to pass current time wherever date time is used.

public Supplier<LocalDateTime> localDateTime = () -> LocalDateTime.now();

and in the test method just override its value like :

myClassObj.localDateTime = () -> LocalDateTime.parse("2020-11-24T23:59:59.999");
Strepitous answered 25/11, 2020 at 23:18 Comment(0)
S
1

You might also want to pass a fixed clock in production (the value of which is fixed at the start of a transaction) to avoid using inconsistent "now" in different entities and requests. See this question for details.

Sectarian answered 26/9, 2015 at 9:35 Comment(0)
A
1

You can mock final classes with Mockito. Add this 'mockito-extensions' directory to your src/test/resources i.e. src/test/resources/mockito-extensions

Add this file

org.mockito.plugins.MockMaker

with content

mock-maker-inline

Mockito will check the extensions directory for configuration files when it is loaded. This file will enable the mocking of final methods and classes.

You can find more details on this approach using baeldung

Another programmatic approach is using MockMakers.INLINE in your code as shown in the official example:

Mockito.mock(ClassWithFinalMethod.class, withSettings().mockMaker(MockMakers.INLINE));
Mockito.when(inlineMock.finalMethodCallingNonFinal()).thenReturn("MOCKED");
assertEquals("MOCKED", inlineMock.finalMethodCallingNonFinal());

You can also use annotation as described in the docs:

@Mock(mockMaker = MockMakers.INLINE)
Foo mock;
Albert answered 31/1, 2023 at 11:38 Comment(0)
M
0

Using Spring:

ClockConfiguration class:

@Configuration
public class ClockConfiguration {
    private final static LocalDate LOCAL_DATE = LocalDate.of(2019, 12, 17);

    @Bean
    @ConditionalOnMissingBean
    Clock getSystemDefaultZoneClock() {
        return Clock.systemDefaultZone();
    }

    @Bean
    @Profile("test")
    @Primary
    Clock getFixedClock() {
        return Clock.fixed(LOCAL_DATE.atStartOfDay(ZoneId.systemDefault()).toInstant(), ZoneId.systemDefault());
    }
}

SomeService class:

@Service
@RequiredArgsConstructor
public class SomeService {

    private final Clock clock;

    public void someMethod(){
        ...

        LocalDateTime.now(clock)
        LocalDate.now(clock)

        ...
    }
}

You must have an active "test" profile in the test:

SomeServiceTest class:

@ActiveProfiles("test")
@EnableConfigurationProperties
@SpringBootTest(classes = [YourAppMainClass])
class SomeServiceTest {
    ...
}
Menken answered 17/12, 2019 at 12:48 Comment(0)
D
0

to Mock LocalDate to desired date

    @Test
    public void mockStaticMethod() {
        //dummy data
        try(MockedStatic<LocalDate> mockedStatic=Mockito.mockStatic(LocalDate.class,Mockito.CALLS_REAL_METHODS)){
            LocalDate currentDate=LocalDate.of(2023, 1, 11);
            mockedStatic.when(LocalDate::now).thenReturn(currentDate);
          //yourService.serviceMethod(arguments);
          //assertEquals(expected, actual);
        }
    }
Damle answered 11/7, 2023 at 15:42 Comment(1)
It is working for me... Why did someone give it a negative vote?Vicarial
B
-1

given i have a random class with a method that just returns LocalDate.now()

import java.time.LocalDate;

public RandomClass {

    public LocalDate getTodaysDate() {
        return LocalDate.now();
    }
}

and i want to mock that to return my birthday instead

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

import java.time.LocalDate;
import java.time.Month;

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

public class RandomClassTest {

    private RandomClass randomClass;

    private MockedStatic<LocalDate> localDateMockedStatic;

    @BeforeEach
    void setUp() {
        randomClass = new RandomClass();

        // instead of using try-with resources, i decide to initialize the mockedStatic object before each test method
        localDateMockedStatic = Mockito.mockStatic(LocalDate.class, Mockito.CALLS_REAL_METHODS);
    }

    @AfterEach
    void tearDown() {
        localDateMockedStatic.reset();        
        localDateMockedStatic.close(); // and close the mockedStatic after each test method
    }

    @Test
    public void getTodaysDateBirthdayTest() {
        LocalDate birthday = LocalDate.of(1999, Month.SEPTEMBER, 29);

        localDateMockedStatic.when(LocalDate::now).thenReturn(birthday);

        assertEquals(birthday, randomClass.getTodaysDate());
    }

    @Test
    public void getTodaysDateDefaultTest() {
        // due to Mockito.CALLS_REAL_METHODS, this has default functionality
        assertEquals(LocalDate.now(), randomClass.getTodaysDate());
    }
}

this is essentially the same thing as some other responses in this thread but this just looks more visually pleasant to me so this is how i like to do it

Beamon answered 13/7, 2023 at 19:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.