How can I mock java.time.LocalDate.now()

后端 未结 7 1257
迷失自我
迷失自我 2020-12-01 01:02

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

相关标签:
7条回答
  • 2020-12-01 01:41

    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");
    
    0 讨论(0)
  • 2020-12-01 01:45

    You can simply pass the current date as function parameter.

    fun doSmthng(now: LocalDate = LocalDate.now()) {
        testAnotherService.doSmthng1(TestObj("my message!", now))
    }
    

    And in the test you can pass some specific date and assert on it. Idea is to inject the dependencies instead of creating explicitly in the function body.

    0 讨论(0)
  • 2020-12-01 01:50

    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);
    }
    
    0 讨论(0)
  • 2020-12-01 01:50

    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);
    }
    
    0 讨论(0)
  • 2020-12-01 01:57

    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 {
        ...
    }
    
    0 讨论(0)
  • 2020-12-01 02:01

    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.

    0 讨论(0)
提交回复
热议问题