如何使用junit在spring-boot中测试常见错误ErrorController

Posted

技术标签:

【中文标题】如何使用junit在spring-boot中测试常见错误ErrorController【英文标题】:How to test common error ErrorController in spring-boot with junit 【发布时间】:2017-08-03 07:52:58 【问题描述】:

在一个 Spring Boot 项目中,我想用 Junit 测试我的 ErrorController。 代码如下sn-p。

@RestController
public class ApiErrorController implements ErrorController 
  private static final Logger LOGGER = LoggerFactory.getLogger(ApiErrorController.class);

  @Value("$server.error.path")
  private String errorPath;

  @Override
  public String getErrorPath() 
    return this.errorPath;
  

  @RequestMapping("/error")
  public ResponseEntity<ErrorResult> error(HttpServletRequest request, HttpServletResponse response) 

    String requestURI = (String) request.getAttribute("javax.servlet.forward.request_uri");
    LOGGER.info("error handling start url = ", requestURI);

    String servletMessage = (String) request.getAttribute("javax.servlet.error.message");
    Integer servletStatus = (Integer) request.getAttribute("javax.servlet.error.status_code");

    String[] messages = new String[0];
    if (!StringUtils.isNullOrEmpty(servletMessage)) 
      messages = new String[]  servletMessage ;
    
    HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
    try 
      if (servletStatus != null && servletStatus instanceof Integer) 
        status = HttpStatus.valueOf(servletStatus);
      
     catch (Exception ex)  // test this exception
      LOGGER.warn("http status not converted.", request.getAttribute("javax.servlet.error.status_code"), ex);
    
    ErrorResult body = new ErrorResult();
    body.setMessages(messages);
    ResponseEntity<ErrorResult> responseResult = new ResponseEntity<>(body, status);
    return responseResult;
  

当我的 Controller(例如 AbcController)发生业务异常时,程序进入 ExceptionControllerAdvice 类。 如果 ExceptionControllerAdvice 发生异常,则程序进入上述 ApiErrorController 类。

有人能告诉我如何测试HttpStatus.valueOf(servletStatus) 失败的情况吗? 另外,我希望request.getAttribute("javax.servlet.error.message") 返回一个非空字符串。 如何实现我想测试的?

顺便说一下,我不想只测试error方法的逻辑。我想使用我提到的AbcController 进行测试。我想要的是当AbcController发生错误时,那么ApiErrorController中的error方法可以成功处理。

追加: 比如ExceptionControllerAdvice会处理业务异常。

@ControllerAdvice(annotations = RestController.class)
public class ExceptionControllerAdvice 
  private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionControllerAdvice.class);

  @ExceptionHandler( BusinessCloudException.class )
  public ResponseEntity<ErrorResult> handleBlCloudException(HttpServletRequest request, HttpServletResponse response,
                                                            BlCloudException ex) 

    HttpStatus status = ErrorUtils.toHttpStatus(ex.getType());
    ErrorResult body = new ErrorResult();
    body.setMessages(ex.getMessageArray());
    ResponseEntity<ErrorResult> responseResult = new ResponseEntity<>(body, status);
    return responseResult;
  

如果handleBlCloudException方法发生错误,则程序进入ApiErrorController处理该错误。

程序如何生成特定的servletStatusjavax.servlet.error.message?如何模拟来做到这一点?

【问题讨论】:

【参考方案1】:

首先,该错误方法发生了很多事情。您可能会考虑将一些逻辑转移到专门的类/公共方法中。

除此之外,我建议使用 Mockito。

首先创建一个封装HttpStatus检索的方法:

HttpStatus getHttpStatusByServletStatus(Integer servletStatus)
    return HttpStatus.valueOf(servletStatus);

并将您的代码更改为:

  if (servletStatus != null && servletStatus instanceof Integer) 
    status = getHttpStatusByServletStatus(servletStatus);
  

然后是测试类:

公共 ApiErrorControllerTest

 @Spy
 private ApiErrorController apiErrorController; 

 @Mock
 HttpServletRequest requestMock;
 @Mock
 HttpServletResponse responseMock;

 @Befire
 public void init()
    MockitoAnnotations.initMocks(this);
 

 @Test
 public void test()
    // Arrange

   HttpStatus expectedStatus = // expected status
   String expectedErrorMessage = // ..

   doReturn(expectedStatus).when(apiErrorController)
     .getHttpStatusByServletStatus(Mockito.anyString());

   when(requestMock.getAttribute("javax.servlet.error.message"))
     .thenReturn(expectedErrorMessage);

   // other setup.. 

   // Act
   apiErrorController.error(requestMock, responseMock);

   // Assertions
 

【讨论】:

谢谢。但我不想只测试error 方法的逻辑。我想使用我提到的AbcController 进行测试。我想要的是当AbcController中发生错误时,那么ApiErrorController中的error方法可以成功处理。 那么基本上你想写一个集成测试? 是的,你可以这么想。也许它看起来像一个集成测试。 好吧,如果没有这里的 impl,我不能说任何话......你不能重现那个场景的问题是什么?

以上是关于如何使用junit在spring-boot中测试常见错误ErrorController的主要内容,如果未能解决你的问题,请参考以下文章

如何在spring-boot应用程序的junit中调用ApplicationContextInitializer

spring-boot2.0 单元测试JUnit4

Spring-boot,使用不同配置文件的 JUnit 测试

在 junit 测试中禁用 RedisHttpSessionConfiguration

基于spring-boot的应用程序的单元+集成测试方案

Maven 依赖于 Spring Boot 和 Junit