'Global exception handling in spring webflux during tests

I'm developing a service with spring webflux. I implemented exception handling using @ControllerAdvice. It works pretty well, but when I run integration tests it seems that @ControllerAdvice annotated component is not loaded, resulting in this response:

{
   "timestamp":"2019-11-28T08:56:47.285+0000",
   "path":"/fooController/bar",
   "status":500,
   "error":"Internal Server Error",
   "message":"java.lang.IllegalStateException: Could not resolve parameter [1] in protected org.springframework.http.ResponseEntity<it.test.model.Response> it.test.exception.ExceptionHandlerController.handleServiceException(java.lang.Exception,org.springframework.web.context.request.WebRequest): No suitable resolver
}

This is my controller advice:

@ControllerAdvice
public class ExceptionHandlerController extends ResponseEntityExceptionHandler {

    private final Logger logger = LoggerFactory.getLogger(ExceptionHandlerController.class);

    @ExceptionHandler
    protected ResponseEntity<Response> handleServiceException(Exception ex, WebRequest request) {

        this.logger.error("Error occurred: \"{}\"", ex.getMessage());

        Response<Foo> response = new Response<>(new Foo(),
                "generic error",
                HttpStatus.INTERNAL_SERVER_ERROR);

        return new ResponseEntity<>(response, null, HttpStatus.OK);
    }
}

And this is my integration test class

@ExtendWith(SpringExtension.class)
@WebFluxTest(MyController.class)
@ContextConfiguration(classes = {MyController.class, MyServiceImpl.class, ExceptionHandlerController.class })
public class MyControllerIT {

    @Autowired
    private WebTestClient webTestClient;

    @Test
    public void testShouldFail() throws IOException {

        return this.webTestClient.get()
            .uri(uri)
            .accept(MediaType.APPLICATION_JSON)
            .exchange()
            .expectStatus().isOk()
            .expectBody()
            .jsonPath("$.statusCode").isEqualTo(500);
    }
}


Solution 1:[1]

if you read the documentation for @WebFluxTest it states:

Annotation that can be used for a Spring WebFlux test that focuses only on Spring WebFlux components.

Using this annotation will disable full auto-configuration and instead apply only configuration relevant to WebFlux tests (i.e. @Controller, @ControllerAdvice, @JsonComponent, Converter/GenericConverter, and WebFluxConfigurer beans but not @Component, @Service or @Repository beans).

Typically @WebFluxTest is used in combination with @MockBean or @Import to create any collaborators required by your @Controller beans.

If you are looking to load your full application configuration and use WebTestClient, you should consider @SpringBootTest combined with @AutoConfigureWebTestClient rather than this annotation.

This means that

@ContextConfiguration(classes = {MyController.class, MyServiceImpl.class, ExceptionHandlerController.class })

is not what u use here. The @WebFluxTest annotation does not load @Component, @Service or @Repository

It is mainly used to test RestControllers only, and their advices.

The options you have seem to be:

  • loading MyController.class and then mock any dependency this class has (MyServiceImpl.class)
  • load a full Context using @SpringBootTest instead combined with @AutoConfigureWebTestClient

Solution 2:[2]

Just a not on top of the already accepted answer which is totally correct. The usage of @ContextConfiguration is necessary only for testing Functional endpoints. I Think this is probably what confuses people.

@WebFluxTest is used during functional endpoint tests for starting the web context and server. But since we dont use controllers, we have to use @ContextConfiguration to bring the handler and routerFunction beans in our spring context.

In this case since we use annotated controller a simple @WebFluxTest(Controller.class) is more than enough for unit testing.

Solution 3:[3]

Adding controllerAdvice in the WebTestClient solved the issue.

WebTestClient.bindToController(controller)
        .controllerAdvice(errorControllerAdvice)
        .build()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Toerktumlare
Solution 2 homeOfTheWizard
Solution 3 SANN3