代码之家  ›  专栏  ›  技术社区  ›  Volkan Yazıcı

Spring MVC测试中的空异常体

  •  12
  • Volkan Yazıcı  · 技术社区  · 10 年前

    我在尝试使MockMvc在响应主体中包含异常消息时遇到了问题。我的控制器如下:

    @RequestMapping("/user/new")
    public AbstractResponse create(@Valid NewUserParameters params, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) throw BadRequestException.of(bindingResult);
        // ...
    }
    

    哪里 BadRequestException 看起来像这样:

    @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "bad request")
    public class BadRequestException extends IllegalArgumentException {
    
        public BadRequestException(String cause) { super(cause); }
    
        public static BadRequestException of(BindingResult bindingResult) { /* ... */ }
    
    }
    

    我进行了以下测试 /user/new 控制器:

    @Test
    public void testUserNew() throws Exception {
        getMockMvc().perform(post("/user/new")
                .param("username", username)
                .param("password", password))
                .andDo(print())
                .andExpect(status().isOk());
    }
    

    它打印以下输出:

      Resolved Exception:
                    Type = controller.exception.BadRequestException
    
            ModelAndView:
               View name = null
                    View = null
                   Model = null
    
                FlashMap:
    
    MockHttpServletResponse:
                  Status = 400
           Error message = bad request
                 Headers = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY]}
            Content type = null
                    Body = 
           Forwarded URL = null
          Redirected URL = null
                 Cookies = []
    

    有人知道为什么 Body 中缺少 print() 输出

    编辑: 我没有使用任何自定义异常处理程序,当我运行服务器时,代码可以按预期工作。也就是说,运行应用程序并向服务器发出相同的请求会返回

    {"timestamp":1423076185822,
     "status":400,
     "error":"Bad Request",
     "exception":"controller.exception.BadRequestException",
     "message":"binding failed for field(s): password, username, username",
     "path":"/user/new"}
    

    正如预期的那样。因此 MockMvc 我想。它不知怎么错过了捕捉 message 字段,而常规应用程序服务器的默认异常处理程序按预期工作。

    3 回复  |  直到 10 年前
        1
  •  10
  •   Volkan Yazıcı    10 年前

    打开后 ticket 对于这个问题,我被告知,主体中的错误消息由Spring Boot负责,它在Servlet容器级别配置错误映射,并且由于Spring MVC测试使用模拟Servlet请求/响应运行,因此不存在这样的错误映射。此外,他们建议我至少创建一个 @WebIntegrationTest 并坚持对我的控制器逻辑进行SpringMVC测试。

    最后,我决定使用自己的自定义异常处理程序,并坚持 MockMvc 其余的和以前一样。

    @ControllerAdvice
    public class CustomExceptionHandler {
    
        @ExceptionHandler(Throwable.class)
        public @ResponseBody
        ExceptionResponse handle(HttpServletResponse response, Throwable throwable) {
            HttpStatus status = Optional
                    .ofNullable(AnnotationUtils.getAnnotation(throwable.getClass(), ResponseStatus.class))
                    .map(ResponseStatus::value)
                    .orElse(HttpStatus.INTERNAL_SERVER_ERROR);
            response.setStatus(status.value());
            return new ExceptionResponse(throwable.getMessage());
        }
    
    }
    
    @Data
    public class ExceptionResponse extends AbstractResponse {
    
        private final long timestamp = System.currentTimeMillis();
    
        private final String message;
    
        @JsonCreator
        public ExceptionResponse(String message) {
            checkNotNull(message, "message == NULL");
            this.message = message;
        }
    
    }
    
        2
  •  3
  •   Master Slave    10 年前

    这可能意味着你要么没有处理异常,要么你真的让身体空了。要处理异常,请在控制器中添加错误处理程序

    @ExceptionHandler
    public @ResponseBody String handle(BadRequestException e) {
        return "I'm the body";
    }
    

    或使用全局错误处理程序(如果是3.2或更高版本)

    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        @ExceptionHandler
        public @ResponseBody String handleBadRequestException(BadRequestException ex) {
            return "I'm the body";
        }
    }
    

    这样将填充正文,您应该用错误消息填充它

        3
  •  0
  •   snovelli    4 年前

    更新的解决方案:

    如果您不想进行完整的集成测试,但仍想确保消息符合预期,则仍可以执行以下操作:

    String errorMessage = getMockMvc()
                            .perform(post("/user/new"))
                            ...
                            .andReturn().getResolvedException().getMessage();
    
    assertThat(errorMessage, is("This is the error message!");