Spring Boot中使用Clock.fixed()模拟LocalDate.now()失效问题排查
问题根源分析
你遇到的核心问题是:测试中Mock的Clock根本没被Spring容器里的Service用到。
让我拆解一下为什么会这样:
- 你用
@SpringBootTest启动了完整的Spring上下文,Controller和SavingAccountService都是由Spring容器管理的Bean。 - 你通过
@InjectMocks创建的SavingAccountService只是测试类里的一个本地对象,和容器中Controller依赖的那个Service完全是两个实例。 - 当你用
TestRestTemplate发请求到Controller时,Controller调用的是容器里的Service,而这个Service注入的还是你在@SpringBootApplication中定义的默认ClockBean,自然拿到的是当前系统时间。
解决方案(推荐方式)
用@MockBean替换@Mock,让Spring自动把容器里的Clock Bean换成你Mock的实例,这样整个上下文里的Service都会使用这个Mock的Clock:
@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = RANDOM_PORT, classes = ChallengeApplication.class) @ActiveProfiles("test") public class SavingAccountTest { @MockBean // 替换成@MockBean,让Spring容器感知到这个Mock private Clock clock; private Clock fixedClock; @Autowired // 直接从容器注入Service,不用@InjectMocks private SavingAccountService savingAccountService; @Autowired private TestRestTemplate restTemplate; private URL base; @LocalServerPort int port; @Before public void setUp() throws MalformedURLException { base = new URL("http://localhost:" + port + "/your-api-endpoint"); final LocalDate WEEKEND = LocalDate.of(2020, 07, 05); fixedClock = Clock.fixed(WEEKEND.atTime(9, 5).toInstant(ZoneOffset.UTC), ZoneId.of("CET")); // 配置Mock的Clock返回固定值 when(clock.instant()).thenReturn(fixedClock.instant()); when(clock.getZone()).thenReturn(fixedClock.getZone()); } // 你的测试方法... }
补充说明
@MockBean是Spring Boot提供的测试注解,专门用于在集成测试中替换Spring上下文里的Bean,这是官方推荐的容器内Mock方式。- 不要在
@SpringBootTest这种启动完整上下文的测试中混合使用@InjectMocks和容器注入,很容易出现实例不匹配的问题。 - 如果你之前的
@InjectMocks是为了测试Service的逻辑,建议单独写单元测试(用@ExtendWith(MockitoExtension.class),不启动Spring上下文),而集成测试专注于验证整个调用链的正确性。
内容的提问来源于stack exchange,提问作者liviubiur
相关产品推荐
相关产品推荐

