我正在使用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
。