98

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

assylias
  • 321,522
  • 82
  • 660
  • 783
Neil
  • 2,714
  • 11
  • 29
  • 45

11 Answers11

165

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);
}
SilverNak
  • 3,283
  • 4
  • 28
  • 44
assylias
  • 321,522
  • 82
  • 660
  • 783
  • 8
    Is there a way to mock without changing code(LocalDateTime.now()) and Without using Powermock? – Shivaraja HN Aug 28 '19 at 05:05
  • 2
    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 :-) – BitfulByte Oct 07 '19 at 10:06
  • 3
    @PimHazebroek the @ Bean is used for the production code not for the unit test – ihebiheb Oct 08 '19 at 15:29
  • 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 date – yatinbc Mar 03 '20 at 15:06
  • This is a proof of a bad [tag:java-time] design. – Nikolas Charalambidis Dec 21 '20 at 12:28
  • 2
    Please note that as for Mockito 4.4.0 it is already possible to mock static methods. See https://stackoverflow.com/a/71530773/9126244 – hfc Mar 18 '22 at 17:13
10

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.

Community
  • 1
  • 1
Aivean
  • 10,692
  • 25
  • 39
7

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));
        }
    }
user2420898
  • 89
  • 1
  • 2
  • 2
    Downvote because this destroys the rest of LocalDate functionality (e.g. minusDays, parse) Using Mockito to mock Clock.class instead worked, see https://stackoverflow.com/a/70186748/2080575 – Alex R Jun 10 '22 at 14:45
  • 1
    you can also mock just @Before one of the test and close it later. https://stackoverflow.com/questions/65965396/mockito-3-6-using-mockstatic-in-before-or-beforeclass-with-junit4 – ikhvjs Aug 28 '22 at 11:19
  • @AlexR that's really foul. mockito lets you choose the default response when invoking a non-mocked static method. `MockedStatic 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 normal – vesper Jul 13 '23 at 19:23
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);
}
JuanMoreno
  • 2,498
  • 1
  • 25
  • 34
  • 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? – Damian Feb 05 '19 at 16:17
  • @Damian 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: http://jmockit.github.io/tutorial/Introduction.html#maven – JuanMoreno Feb 05 '19 at 18:18
  • 1
    I tried the powermock alternative and it does not work. – carlos palma Jul 31 '19 at 22:40
  • @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: https://github.com/eugenp/tutorials/blob/master/core-java-modules/core-java-8/src/test/java/com/baeldung/time/LocalDateTimeUnitTest.java – JuanMoreno Aug 01 '19 at 15:18
  • The dependencies for PowerMock 2 sample are here: https://github.com/eugenp/tutorials/blob/master/core-java-modules/core-java-8/pom.xml#L108-L118 – JuanMoreno Aug 01 '19 at 16:56
  • @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. – carlos palma Aug 01 '19 at 20:34
  • 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? – Jolley71717 Dec 04 '19 at 22:37
  • @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? – JuanMoreno Dec 05 '19 at 12:04
  • @SHoko, I did call `mockStatic(LocalDate.class)` first. I'm using PowerMock version 2.0.0-beta.5 – Jolley71717 Dec 05 '19 at 18:13
  • @Jolley71717 Could you try a newer version like 2.0.0-RC.4 or 2.0.0?. This samples run fine in that versions. – JuanMoreno Dec 05 '19 at 22:09
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");
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.

Community
  • 1
  • 1
Gebb
  • 6,371
  • 3
  • 44
  • 56
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;
Adam
  • 2,800
  • 1
  • 13
  • 15
1

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.

Beast77
  • 193
  • 1
  • 17
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 {
    ...
}
Strax
  • 91
  • 1
  • 5
-1

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);
        }
    }
Aayush Bhattacharya
  • 1,628
  • 18
  • 17
-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

starball
  • 20,030
  • 7
  • 43
  • 238
vesper
  • 52
  • 6