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

如何用Mockito测试WebClient的5xx/4xx错误分支及目标方法?

Testing WebClient Error Scenarios with Mockito (5xx/4xx Errors & ClientResponse Handling)

Great news—you don't need PowerMock for any of these scenarios! We can handle all your testing goals using standard Mockito and Spring WebFlux's built-in utilities. Let's break down each problem with concrete code examples:

1. Mocking 5xx Server Error Responses

To trigger the HttpStatus::is5xxServerError branch, we need to target the exact status predicate in our stub and simulate a valid 5xx ClientResponse:

@Test
void when5xxErrorOccurs_aMethodIsCalledAndCustomExceptionThrown() {
    // 1. Build a realistic 5xx ClientResponse using Spring's built-in builder
    ClientResponse mock5xxResponse = ClientResponse.create(HttpStatus.INTERNAL_SERVER_ERROR)
            .header("X-Request-ID", "test-123") // Add headers if your method uses them
            .build();

    // 2. Stub the WebClient response chain to trigger the 5xx handler
    ClassUnderTest classUnderTestSpy = spy(new ClassUnderTest());
    doReturn(responseSpec).when(classUnderTestSpy).requestStuff();

    // Match the exact 5xx predicate instead of using generic any()
    when(responseSpec.onStatus(HttpStatus::is5xxServerError, any()))
            .thenAnswer(invocation -> {
                // Extract the error handler lambda from the method call
                Function<ClientResponse, Mono<? extends Throwable>> errorHandler = 
                        invocation.getArgument(1);
                // Invoke the handler with our mock response to trigger the logic
                errorHandler.apply(mock5xxResponse);
                return responseSpec;
            });

    // 3. Verify the reactive stream emits the expected error
    StepVerifier.create(classUnderTestSpy.doStuff())
            .expectErrorMatches(throwable -> 
                    throwable instanceof MyCustomException &&
                    throwable.getMessage().equals("First error I would like to test"))
            .verify();

    // Confirm aMethodIWouldLikeToTest was called with the correct response
    verify(classUnderTestSpy).aMethodIWouldLikeToTest(mock5xxResponse);
}

2. Mocking 4xx Client Error Responses

This follows the same pattern as the 5xx case—we just switch the status predicate and response code:

@Test
void when4xxErrorOccurs_aMethodIsCalledAndCustomExceptionThrown() {
    // 1. Build a 4xx ClientResponse (e.g., BAD_REQUEST)
    ClientResponse mock4xxResponse = ClientResponse.create(HttpStatus.BAD_REQUEST)
            .body("Invalid input payload") // Add body content if needed
            .build();

    // 2. Stub the ResponseSpec for 4xx errors
    ClassUnderTest classUnderTestSpy = spy(new ClassUnderTest());
    doReturn(responseSpec).when(classUnderTestSpy).requestStuff();

    when(responseSpec.onStatus(HttpStatus::is4xxClientError, any()))
            .thenAnswer(invocation -> {
                Function<ClientResponse, Mono<? extends Throwable>> errorHandler = 
                        invocation.getArgument(1);
                errorHandler.apply(mock4xxResponse);
                return responseSpec;
            });

    // 3. Verify error and method invocation
    StepVerifier.create(classUnderTestSpy.doStuff())
            .expectErrorMatches(throwable -> 
                    throwable instanceof MyCustomException &&
                    throwable.getMessage().equals("Second error I would like to test"))
            .verify();

    verify(classUnderTestSpy).aMethodIWouldLikeToTest(mock4xxResponse);
}

3. Testing aMethodIWouldLikeToTest Directly

If you want to validate the logic inside aMethodIWouldLikeToTest in isolation (beyond just verifying it's called), you can call it directly with pre-built ClientResponse instances:

@Test
void aMethodIWouldLikeToTest_handlesClientResponsesCorrectly() {
    // Create multiple response variants to cover edge cases
    ClientResponse internalServerError = ClientResponse.create(HttpStatus.INTERNAL_SERVER_ERROR)
            .header("X-Error-Code", "500-001")
            .build();

    ClientResponse unauthorized = ClientResponse.create(HttpStatus.UNAUTHORIZED)
            .body("Invalid API key")
            .build();

    ClassUnderTest classUnderTest = new ClassUnderTest();

    // Test with 5xx response
    classUnderTest.aMethodIWouldLikeToTest(internalServerError);
    // Add assertions based on what the method does (e.g., log entries, metric updates)

    // Test with 4xx response
    classUnderTest.aMethodIWouldLikeToTest(unauthorized);
    // Add relevant assertions for this scenario
}

Key Takeaways:

  • No mocking of ClientResponse needed: Spring WebFlux's ClientResponse.create() lets you build realistic, testable response objects without mocking the entire interface.
  • StepVerifier is your friend: It's the standard tool for validating reactive stream behavior in Spring, ensuring errors are emitted as expected.
  • thenAnswer() for lambda extraction: This lets us trigger the actual error handler logic from your production code, making the test behave just like a real WebClient error response.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:07:22