如何用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
ClientResponseneeded: Spring WebFlux'sClientResponse.create()lets you build realistic, testable response objects without mocking the entire interface. StepVerifieris 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
相关产品推荐
相关产品推荐

