You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot WebFlux测试中Clock Mock引发空指针问题求助

修复WebFluxTest中Mock Clock导致的NullPointerException问题

你遇到的空指针问题,根源在于通过Mockito静态mock Clock类的方式干扰了Spring WebFlux内部组件的初始化。当调用webTestClient.mutate().build()时,Spring会重新构建WebHttpHandlerAdapter并创建InMemoryWebSessionStore,此时你mock的Clock.systemUTC()返回的是一个未完全实现的Spy Clock实例——由于Clock是接口,直接spy接口无法生成完整的实现逻辑,导致Spring内部调用Clock方法时触发空指针。

这种静态mock的侵入性极强,会影响所有依赖Clock.systemUTC()的代码(包括Spring框架自身组件),这也是不用WebTestClient的测试能正常运行,但使用后报错的核心原因。


修复方案:用Spring依赖注入替换静态Mock

正确的做法是利用Spring的Bean替换机制,提供自定义的Clock Bean,让所有Spring管理的组件(包括业务代码和WebFlux内部会话存储)都使用你指定的Clock,彻底避免静态Mock的副作用。

修改后的测试代码:

@ExtendWith(SpringExtension.class)
@WebFluxTest(controllers = MyController.class)
@Import({MyController.class})
public class Test implements WebTestClientConfigurable {

    @Autowired
    private MyController myController;
    @Autowired
    private WebTestClient webTestClient;
    // 固定测试时间(可根据需求调整)
    private static final Instant TEST_NOW = Instant.ofEpochMilli(System.currentTimeMillis());

    // 自定义Clock Bean,覆盖默认系统Clock
    @TestConfiguration
    static class TestClockConfig {
        @Bean
        public Clock clock() {
            // 返回固定时间的Clock实例
            return Clock.fixed(TEST_NOW, ZoneOffset.UTC);
        }
    }

    @org.junit.jupiter.api.Test
    void test() {
        configure(webTestClient)
                .post()
                .uri("/test")
                .exchange()
                .expectStatus()
                .is2xxSuccessful();
    }
}

动态调整时间的扩展方案:

如果需要在测试中动态修改时间,可以给Clock Bean增加可变性,比如用原子引用包装时间:

@TestConfiguration
static class TestClockConfig {
    private final AtomicReference<Instant> currentInstant = new AtomicReference<>(TEST_NOW);

    @Bean
    public Clock clock() {
        return new Clock() {
            @Override
            public Instant instant() {
                return currentInstant.get();
            }

            @Override
            public ZoneId getZone() {
                return ZoneOffset.UTC;
            }

            @Override
            public Clock withZone(ZoneId zone) {
                return Clock.fixed(currentInstant.get(), zone);
            }
        };
    }

    // 提供方法动态更新时间
    public void setCurrentInstant(Instant newInstant) {
        currentInstant.set(newInstant);
    }
}

在测试中注入TestClockConfig,调用setCurrentInstant()即可随时调整时间。


内容的提问来源于stack exchange,提问作者marie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 01:45:23