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

如何用Spock测试AService类函数及Mono.flatMap?

Fixing Spock Test for AService with Mono.flatMap (Invocation Count = 0 Issue)

Ah, I’ve been there before—reactive testing with Spock can trip you up with that lazy execution gotcha! Let’s figure out why your service.method(ApiResource.GOOD, b) invocation count is showing 0 and fix it step by step.

1. The #1 Culprit: You’re Not Triggering the Mono Execution

Reactive streams like Mono are lazy by default. That means unless you explicitly subscribe to the stream or block on it, none of the code inside operators like flatMap will actually run. If your test just calls the method that returns a Mono and stops there, the code inside flatMap (including your target method call) never gets executed—hence the 0 invocation count.

You have two solid options to fix this:

  • Use .block() on the Mono to wait for it to complete (simple for unit tests)
  • Use StepVerifier from Reactor Test to subscribe and validate the stream (more idiomatic for reactive code)

2. Double-Check Your Mock Setup & Parameter Matching

Even if you trigger the stream, your mock might not be capturing the call if:

  • The mock isn’t properly injected into your AService instance
  • Your parameter matchers are too strict (or not strict enough)
    • For example, if b is an object, make sure you’re using eq(b) for exact equality, or any() if you don’t need to match the exact instance
    • Verify that ApiResource.GOOD is indeed the constant being passed in your flatMap logic

Full Working Example

Let’s put this all together with concrete code to make it clear.

Sample AService Implementation

import reactor.core.publisher.Mono;

public class AService {
    private final DependencyService dependencyService;

    public AService(DependencyService dependencyService) {
        this.dependencyService = dependencyService;
    }

    public Mono<String> process(String input) {
        return Mono.just(input)
                .flatMap(a -> {
                    // Some logic to transform input to 'b'
                    String b = input.trim().toUpperCase();
                    // This is the call we want to verify
                    return dependencyService.method(ApiResource.GOOD, b);
                });
    }
}

enum ApiResource {
    GOOD, BAD
}

interface DependencyService {
    Mono<String> method(ApiResource resource, String value);
}

Corrected Spock Test

import reactor.core.publisher.Mono
import reactor.test.StepVerifier
import spock.lang.Specification

class AServiceSpec extends Specification {

    // Mock the dependency service
    DependencyService dependencyService = Mock()
    // Inject mock into AService
    AService aService = new AService(dependencyService)

    def "process() should call dependencyService.method with ApiResource.GOOD"() {
        given:
        String testInput = "  hello  "
        String expectedB = "HELLO"
        // Mock the dependency's method to return a success Mono
        dependencyService.method(ApiResource.GOOD, expectedB) >> Mono.just("processed")

        when:
        Mono<String> resultStream = aService.process(testInput)

        then:
        // Use StepVerifier to trigger the stream and validate output
        StepVerifier.create(resultStream)
                .expectNext("processed")
                .verifyComplete()

        // Verify the mock was called exactly once with the right parameters
        1 * dependencyService.method(ApiResource.GOOD, expectedB)

        // Alternative: Use block() if you don't need to verify the stream output
        // resultStream.block()
        // 1 * dependencyService.method(ApiResource.GOOD, expectedB)
    }
}

Quick Additional Checks

  • If your flatMap has conditional logic, make sure your test input is hitting the branch that calls service.method(ApiResource.GOOD, b)
  • Add error verification (e.g., .expectError() in StepVerifier) if there’s a chance of silent failures in the reactive pipeline
  • Ensure your mock isn’t being reset or replaced somewhere in your test setup

That should get your invocation count showing 1 instead of 0—remember, reactive code doesn’t run until you subscribe to it!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 09:17:46