测试期间 spring webflux 中的全局异常处理

Global exception handling in spring webflux during tests

我正在使用 spring webflux 开发一项服务。我使用 @ControllerAdvice 实现了异常处理。它工作得很好,但是当我 运行 集成测试时,似乎 @ControllerAdvice 注释组件没有加载,导致这个响应:

{
   "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
}

这是我的控制器建议:

@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);
    }
}

这是我的集成测试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);
    }
}

如果您阅读 @WebFluxTestdocumentation,它指出:

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.

这意味着

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

不是你在这里使用的。 @WebFluxTest 注释不加载 @Component@Service@Repository

主要用于测试RestControllers,以及他们的建议。

您的选项似乎是:

  • 加载 MyController.class 然后模拟此 class 具有的任何依赖项 (MyServiceImpl.class)
  • 使用 @SpringBootTest 加载完整上下文,而不是结合 @AutoConfigureWebTestClient

只是不在已经接受的答案之上,这是完全正确的。 只有在测试功能端点时才需要使用 @ContextConfiguration。我想这可能是人们困惑的地方。

@WebFluxTest 在功能端点测试期间用于启动 Web 上下文和服务器。 但是由于我们不使用控制器,我们必须使用@ContextConfiguration 将处理程序和 routerFunction bean 引入我们的 spring 上下文中。

在这种情况下,因为我们使用带注释的控制器,所以一个简单的@WebFluxTest(Controller.class) 就足以进行单元测试了。

在 WebTestClient 中添加 controllerAdvice 解决了问题。

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