如何用Spock测试AService类函数及Mono.flatMap?
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
StepVerifierfrom 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
AServiceinstance - Your parameter matchers are too strict (or not strict enough)
- For example, if
bis an object, make sure you’re usingeq(b)for exact equality, orany()if you don’t need to match the exact instance - Verify that
ApiResource.GOODis indeed the constant being passed in yourflatMaplogic
- For example, if
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
flatMaphas conditional logic, make sure your test input is hitting the branch that callsservice.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

