我如何模拟java.time.LocalDate.now()

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

在我的测试案例中,我需要测试时间敏感的方法,在该方法中,我们使用的是Java 8类LocalDate,它不是Joda。

运行测试时,我该怎么做才能更改时间


在代码中,将LocalDate.now()替换为LocalDate.now(clock);

然后,您可以通过Clock.systemDefaultZone()进行生产,并通过固定的时钟进行测试。

这是一个例子:

首先,注入Clock。如果您使用的是Spring Boot,请执行以下操作:

1
2
3
4
@Bean
public Clock clock() {
    return Clock.systemDefaultZone();
}

其次,在您的代码中调用LocalDate.now(clock)

1
2
3
4
5
6
7
8
9
10
@Component
public class SomeClass{

    @Autowired
    private Clock clock;

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

现在,在单元测试类中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 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);
}


您可以重构代码以使其易于测试,例如,将LocalDate.now()的所有调用替换为自定义可模拟非静态类的某些方法的调用。

另外,您可以使用PowerMock的模拟静态。


如果我们需要模拟像now()这样的静态方法,我们可以使用PowerMock等多种替代方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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);
    }
}

或JMockit,实际上,通过JMockit,我们可以使用MockUp类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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);
}

或Expectations类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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);
}

我们可以在这里找到更多示例。

另一个简单的选择是将now()方法与固定的Clock实例一起使用。当然,java.time包中的大多数类都具有带有Clock参数的now()方法:

1
2
3
4
5
6
7
8
9
@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);
}


您可能还希望在生产中传递一个固定的时钟(该值在事务开始时是固定的),以避免在不同的实体和请求中使用不一致的"现在"。
有关详细信息,请参见此问题。


使用Spring:

ClockConfiguration类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
@RequiredArgsConstructor
public class SomeService {

    private final Clock clock;

    public void someMethod(){
        ...

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

        ...
    }
}

您必须在测试中具有有效的"测试"配置文件:

SomeServiceTest类:

1
2
3
4
5
6
@ActiveProfiles("test")
@EnableConfigurationProperties
@SpringBootTest(classes = [YourAppMainClass])
class SomeServiceTest {
    ...
}