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

WebFlux+Spock测试中StepVerifier未触发Service方法执行问题

问题根源与解决办法

核心问题有两个:Reactor流是惰性执行的,且你在Spock测试里的断言顺序搞反了

为啥原测试跑失败?

  1. Reactor的数据流只有在被订阅之后才会真正执行,StepVerifier.create()只是创建了验证器,还没触发流运行。
  2. 你现在的写法是先调用result.verifyComplete()触发流执行,等流跑完了,才去注册Spock的Mock断言1 * service.test(_) >> true——这时候调用记录已经生成了,断言才刚加上,当然会提示方法从未被执行。

为啥加block()就能过?

block()是同步阻塞方法,会主动触发流执行,在控制器返回Mono.empty()之前,service.test()已经被调用过了,之后再执行Mock断言,自然能抓到调用记录。但这种写法在生产环境绝对不能用,会阻塞线程,完全违背WebFlux异步非阻塞的设计初衷。

正确的测试写法

把Spock的Mock断言放到流触发执行之前,也就是在verifyComplete()调用前注册好断言,保证流运行时断言已经生效。

调整后的测试代码:

def "Should test abc"() {
    setup:
        def testDto = new TestDto(11L, true)
        // 先注册Mock调用断言
        1 * service.test(_) >> true

    when:
        def result = StepVerifier.create(controller.test(Flux.just(testDto)))

    then:
        result.verifyComplete()
}

或者也可以把断言放到when块里,只要保证在verifyComplete()之前:

def "Should test abc"() {
    setup:
        def testDto = new TestDto(11L, true)

    when:
        // 先注册断言
        1 * service.test(_) >> true
        def result = StepVerifier.create(controller.test(Flux.just(testDto)))
        // 触发流执行
        result.verifyComplete()

    then:
        noExceptionThrown()
}

为啥thenAwait没用?

你之前加的thenAwait只是让验证器等待,但核心问题是断言注册晚了,流已经跑完了才加断言,等再久也抓不到调用记录。只有让Mock断言在流启动前就位,才能正确捕获方法调用。

额外说明

生产环境代码是没问题的,因为Spring WebFlux处理请求时会自动订阅返回的Mono/Flux,触发流执行,所以实际运行一切正常。问题完全出在测试的断言顺序和Reactor的惰性执行特性不匹配上。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 22:40:09