【问题标题】:Global exception handling in spring webflux during tests测试期间spring webflux中的全局异常处理
【发布时间】:2020-03-23 21:50:39
【问题描述】:

我正在使用 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);
    }
}

这是我的集成测试类

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

【问题讨论】:

  • 如果出现错误,您永远不应该返回 HttpStatus.OK。那是糟糕的设计
  • 请您删除@ContextConfiguration(classes = {MyController.class, MyServiceImpl.class, ExceptionHandlerController.class })
  • 意思应该是:http response 200 因为错误被管理所以执行已经成功处理。里面的statusCode表示发生了en错误
  • 如果我删除@ContextConfiguration spring 不会启动

标签: java spring spring-boot spring-cloud spring-webflux


【解决方案1】:

如果您阅读documentation for @WebFluxTest,它会指出:

可用于专注于 Spring WebFlux 测试的注解 在 Spring WebFlux 组件上。

使用此注解将禁用完全自动配置,取而代之的是 仅应用配置 与 WebFlux 测试相关(即@Controller、@ControllerAdvice、 @JsonComponent、Converter/GenericConverter 和 WebFluxConfigurer bean 但不是 @Component、@Service 或 @Repository bean)。

通常@WebFluxTest 与@MockBean 或结合使用 @Import 创建 @Controller 所需的任何协作者 豆子。

如果您希望加载完整的应用程序配置并使用 WebTestClient,您应该考虑将@SpringBootTest 与 @AutoConfigureWebTestClient 而不是这个注解。

这意味着

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

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

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

您的选择似乎是:

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

【讨论】:

  • 第二个选项有效,但现在要自动连接 WebTestClient,我必须使用 @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) 并且它会在其他配置之前启动 tomcat,因此无法连接到嵌入式 redis。你有什么建议吗?如果我不使用 webEnvironment,自动装配会因为没有合格的候选 bean 而失败
  • @SpringBootTest 启动整个应用程序,包括服务器和所有内容。您需要为其分配一个端口,以便当它注入 TestWebclient 时,它可以将该端口提供给 testclient。如果它启动 tomcat,那么你必须在某个地方依赖于 spring.web(来自 spring 的非反应性 web 库)。如果你的类路径中有 web,spring 将优先考虑 web 并启动 tomcat 而不是 netty。你可以用其他方式强制它,但强烈建议如果你正在编写一个 webflux 应用程序,你不应该对 spring web 有任何依赖。
  • 感谢您的回复。我试图删除 spring.web 2 天,但我不知道如何实现这一点。我将此添加到我的 bruild.gradle 中,但我认为它不起作用:configurations { runtime.exclude group: "org.springframework.boot", module: "spring-boot-starter-web" runtime.exclude group: "org.springframework.boot", module: "spring-webmvc"}
  • 我不知道 gradle,如果你有 maven,你可以运行 mvn dependency:tree,这将向你展示 spring web 的吸引力。您永远不需要排除弹簧网。您可能使用了某种不打算与 webflux 一起使用的库,它不支持 webflux。
  • spring-web 是 spring-boot-starter-webflux 的一个依赖。我可以排除它,但它看起来很疯狂,不是吗?
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 2018-11-19
    • 2021-11-12
    • 2019-03-11
    • 2013-04-02
    • 2020-10-31
    • 2016-11-18
    • 2014-07-17
    • 2021-08-19
    • 2018-07-19
    相关资源
    最近更新 更多