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

Micronaut自定义ExceptionHandler在JUnit 5测试中与应用运行时表现不一致的问题排查

Micronaut自定义ExceptionHandler在JUnit 5测试中与应用运行时表现不一致的问题排查

这个问题其实是Micronaut HttpClient在测试场景下的默认行为差异导致的——你直接运行应用时,外部客户端(比如浏览器、Postman)会直接接收ExceptionHandler生成的400响应;但测试里用的Micronaut HttpClient,默认会对4xx/5xx这类错误状态码自动抛出HttpClientResponseException,而不是返回响应对象。

问题原因拆解

你的自定义ExceptionHandler其实已经正常工作了(日志里也打印了In handle method),但测试代码里的assertDoesNotThrow阻止了异常的正常抛出,反而触发了JUnit的断言失败。本质是你误解了测试用HttpClient的行为逻辑——它不会像外部客户端那样直接返回错误响应,而是将错误响应包装成异常抛出。

解决方案(三种可选)

方案1:捕获异常并验证异常中的响应内容

这是最直接的调整方式,去掉assertDoesNotThrow,主动捕获HttpClientResponseException,然后从异常里提取响应信息做断言:

@Test
public void testThrow() {
    log.info("Calling throw endpoint");
    // 捕获预期的异常
    HttpClientResponseException exception = assertThrows(HttpClientResponseException.class, () -> {
        client.toBlocking().exchange("/throw");
    });
    // 验证状态码
    assertEquals(HttpStatus.BAD_REQUEST, exception.getStatus());
    // 验证自定义错误体
    CustomError error = exception.getResponse().body(CustomError.class);
    assertEquals(400, error.code());
    assertEquals("BAD_REQUEST", error.message());
    assertEquals("Bad Request found", error.description());
}

方案2:使用支持错误响应的客户端调用方法

可以使用exchange的重载方法,指定允许接收的错误状态码,或者用retrieve方法直接获取错误响应体:

@Test
public void testThrow() {
    log.info("Calling throw endpoint");
    // 指定接收BAD_REQUEST状态码的响应
    HttpResponse<CustomError> response = client.toBlocking()
            .exchange("/throw", CustomError.class, Argument.of(HttpStatus.class, HttpStatus.BAD_REQUEST));
    
    assertEquals(HttpStatus.BAD_REQUEST, response.getStatus());
    CustomError error = response.body();
    // 其他断言逻辑
}

方案3:修改客户端配置,禁用自动抛出异常

如果希望测试用HttpClient和外部客户端行为一致,可以自定义客户端配置,关闭错误状态码自动抛异常的逻辑:

@MicronautTest
public class TestControllerTest {
    private static final Logger log = LoggerFactory.getLogger(TestControllerTest.class);

    @Inject
    @Client(value = "/test", configuration = TestClientConfig.class)
    HttpClient client;

    // 自定义客户端配置类
    @Singleton
    public static class TestClientConfig implements HttpClientConfiguration {
        @Override
        public Class<? extends Throwable> getDefaultErrorType() {
            return null; // 禁用错误状态码自动抛异常
        }
    }

    @Test
    public void testThrow() {
        log.info("Calling throw endpoint");
        // 现在不会抛出异常,可以直接获取响应
        HttpResponse<CustomError> response = client.toBlocking().exchange("/throw", CustomError.class);
        log.info("Response: {}, {}", response, response.body());
        assertEquals(HttpStatus.BAD_REQUEST, response.getStatus());
        // 验证响应体
        CustomError error = response.body();
        assertEquals(400, error.code());
    }
}

总结

核心是要区分应用端的异常处理逻辑和测试客户端的响应处理逻辑:你的ExceptionHandler已经正确生成了错误响应,只是测试用的HttpClient默认会把错误响应转换成异常抛出,只需要调整测试代码或客户端配置即可匹配预期行为。

备注:内容来源于stack exchange,提问作者toolkit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 08:29:13