使用自定义异常处理测试REST端点



我正在使用Spring微服务(模块)的项目上工作,我想使用MockMvc测试我的REST端点。我的测试工作良好的情况下,请求是有效的,但它不工作时,请求一个无效的url。通过不工作,我的意思是我的自定义异常处理程序(@ControllerAdvice)没有被调用,异常被抛出,测试失败。

异常处理程序和测试类在不同的模块中实现。

通用模块(ExceptionHandler)

@ControllerAdvice
public class CoreExceptionHandler {
    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<ErrorMessageDTO> handleException(Exception ex, HttpServletRequest request) {
        // Getting servlet request URL
        String uri = request.getRequestURI();
        HttpStatus a;
        ErrorMessageDTO errorMessage;
        if (ex instanceof CoreException) {
            CoreException e = (CoreException) ex;
            ...
            errorMessage = new ErrorMessageDTO(e, uri);
        } else {
            errorMessage = new ErrorMessageDTO(ex, uri);
            ...
        }
        return new ResponseEntity<ErrorMessageDTO>(errorMessage, a);
    }
}

country-module

这是我的REST端点和测试类实现的地方。通用模块依赖包含在该模块的pom.xml中,包通过主类扫描。

CountryApplication.java

@EnableCaching
@EnableDiscoveryClient
@EnableAspectJAutoProxy
@SpringBootApplication(scanBasePackages = {
    "com.something1.something2.something3.common.exception",
    "com.something1.something2.something3.common.util.logged",
    "com.something1.something2.something3.country"
})
public class CountryApplication {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(CountryApplication.class, args);
    }
    ...
}

CountryService.java

这是我的Service类中的一个方法

@GetMapping("/{id:\d+}")
public CountryDTO getCountryById(@PathVariable("id") Integer id) throws CoreException {
    Country countryEntity = this.countryRepository.findOne(id);
    // requesting for id that does not exist
    if (countryEntity == null) {
        throw new CoreException(CoreError.ENTITY_NOT_FOUND);
    }
    return this.countryMapper.daoToDto(countryEntity);
}

CountryServiceTest.java

@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureTestDatabase
@RunWith(SpringRunner.class)
public class CountryServiceTest {
    ...
    @Autowired
    private MockMvc mockMvc;
    @Test
    public void getByIdTest() throws Exception {
        // Get by id exists
        mockMvc.perform(get("/2"))
            .andExpect(status().isOk())
            .andExpect(content().contentType(contentType))
            .andDo(print());
        // Get by id not exists. NOT WORKING
          mockMvc.perform(get("/100000"))
            .andExpect(status().isNotFound())
            .andExpect(content().contentType(contentType));
    }
}

正如我上面所描述的,问题是在测试方法的第二次请求时,没有调用CoreExceptionHandler并且测试失败抛出:

NestedServletException: Request processing failed; nested exception is com.something1.something2.something3.common.exception.CoreException .

公共模块的依赖关系配置良好(至少当我在非测试模式下部署时),因为我也将其用于其他事情,加上ExceptionHandler在我不测试时被调用。

另一个奇怪的事情是,当我部署我的测试,Spring Boot的日志显示,CoreExceptionHandler被检测到。就是这条线。Detected @ExceptionHandler methods in coreExceptionHandler

有两个问题,解释如下:

(1) ControllerAdvice未在CountryServiceTest类中为MockMvc对象设置,这可以如下所示完成:

MockMvc mockMvc = standaloneSetup(yourController)
        .setHandlerExceptionResolvers(new CoreExceptionHandler())
        .build();

(2)因为CoreException是由Spring Container的NestedServletException包装的,所以您需要使用exception.getCause()来检查异常,如下所示:

@ControllerAdvice
    public class CoreExceptionHandler {
        @ExceptionHandler(value = Exception.class)
        public ResponseEntity<ErrorMessageDTO> handleException(Exception ex, 
            HttpServletRequest request) {
            // Getting servlet request URL
            String uri = request.getRequestURI();
            HttpStatus a;
            ErrorMessageDTO errorMessage;
            //check with exception cause
            if (ex.getCause() instanceof CoreException) {
                CoreException e = (CoreException) ex;
                ...
                errorMessage = new ErrorMessageDTO(e, uri);
            } else if (ex instanceof CoreException) {
               //this block will be used only when direct CoreException triggered
                CoreException e = (CoreException) ex;
                ...
                errorMessage = new ErrorMessageDTO(e, uri);
            } else {
                errorMessage = new ErrorMessageDTO(ex, uri);
                ...
            }
            return new ResponseEntity<ErrorMessageDTO>(errorMessage, a);
        }
    }

此外,我建议不要在单个泛型方法中处理所有异常类型,这将很难支持/维护,而是使用多个@ExceptionHandler方法/类拆分CoreExceptionHandler

最新更新