Тестирование Spring Метод MVC @ExceptionHandler с Spring MVC Test
У меня есть следующий простой контроллер, чтобы поймать любые неожиданные исключения:
@ControllerAdvice
public class ExceptionController {
@ExceptionHandler(Throwable.class)
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ResponseEntity handleException(Throwable ex) {
return ResponseEntityFactory.internalServerErrorResponse("Unexpected error has occurred.", ex);
}
}
Я пытаюсь написать интеграционный тест, используя Spring MVC Test framework. Это то, что у меня есть до сих пор:
@RunWith(MockitoJUnitRunner.class)
public class ExceptionControllerTest {
private MockMvc mockMvc;
@Mock
private StatusController statusController;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new ExceptionController(), statusController).build();
}
@Test
public void checkUnexpectedExceptionsAreCaughtAndStatusCode500IsReturnedInResponse() throws Exception {
when(statusController.checkHealth()).thenThrow(new RuntimeException("Unexpected Exception"));
mockMvc.perform(get("/api/status"))
.andDo(print())
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$.error").value("Unexpected Exception"));
}
}
Я регистрирую ExceptionController и mock StatusController в инфраструктуре MVC Spring.
В методе тестирования я настраиваю ожидание выброса исключения из StatusController.
Исключение вызывается, но ExceptionController не имеет дело с ним.
Я хочу проверить, что ExceptionController получает исключения и возвращает соответствующий ответ.
Любые мысли о том, почему это не работает, и как я должен делать такой тест?
Спасибо.
Ответы
Ответ 1
У меня была одна и та же проблема, и для меня работает следующее:
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(statusController)
.setControllerAdvice(new ExceptionController())
.build();
}
Ответ 2
Этот код добавит возможность использовать ваши контролируемые исключения советы.
@Before
public void setup() {
this.mockMvc = standaloneSetup(commandsController)
.setHandlerExceptionResolvers(withExceptionControllerAdvice())
.setMessageConverters(new MappingJackson2HttpMessageConverter()).build();
}
private ExceptionHandlerExceptionResolver withExceptionControllerAdvice() {
final ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
@Override
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(final HandlerMethod handlerMethod,
final Exception exception) {
Method method = new ExceptionHandlerMethodResolver(ExceptionController.class).resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(new ExceptionController(), method);
}
return super.getExceptionHandlerMethod(handlerMethod, exception);
}
};
exceptionResolver.afterPropertiesSet();
return exceptionResolver;
}
Ответ 3
Поскольку вы используете автономный тест установки, вам необходимо предоставить обработчик исключений вручную.
mockMvc= MockMvcBuilders.standaloneSetup(adminCategoryController).setSingleView(view)
.setHandlerExceptionResolvers(getSimpleMappingExceptionResolver()).build();
У меня была такая же проблема несколько дней назад, вы можете увидеть мою проблему и решение, на которые я ответил здесь Spring Тест на исключение MVC-контроллера
Надеюсь, что мой ответ поможет вам
Ответ 4
Используйте Spring MockMVC для эмуляции servletContainer до такой степени, что вы можете включить любые тесты фильтрации запросов или обработки исключений в ваш набор модульных тестов.
Вы можете настроить эту настройку следующим образом:
Получено пользовательское исключение RecordNotFound...
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Record not found") //
public class RecordNotFoundException extends RuntimeException {
private static final long serialVersionUID = 8857378116992711720L;
public RecordNotFoundException() {
super();
}
public RecordNotFoundException(String message) {
super(message);
}
}
... и RecordNotFoundExceptionHandler
@Slf4j
@ControllerAdvice
public class BusinessExceptionHandler {
@ExceptionHandler(value = RecordNotFoundException.class)
public ResponseEntity<String> handleRecordNotFoundException(
RecordNotFoundException e,
WebRequest request) {
//Logs
LogError logging = new LogError("RecordNotFoundException",
HttpStatus.NOT_FOUND,
request.getDescription(true));
log.info(logging.toJson());
//Http error message
HttpErrorResponse response = new HttpErrorResponse(logging.getStatus(), e.getMessage());
return new ResponseEntity<>(response.toJson(),
HeaderFactory.getErrorHeaders(),
response.getStatus());
}
...
}
Настроить настраиваемый контекст теста: установите @ContextConfiguration, чтобы указать классы, необходимые для теста. Установите Mockito MockMvc в качестве эмулятора контейнера сервлета и настройте тестовую привязку и зависимости.
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {
WebConfig.class,
HeaderFactory.class,
})
@Slf4j
public class OrganisationCtrlTest {
private MockMvc mvc;
private Organisation coorg;
@MockBean
private OrganisationSvc service;
@InjectMocks
private OrganisationCtrl controller = new OrganisationCtrl();
//Constructor
public OrganisationCtrlTest() {
}
....
Настройте макет MVC "эмулятор сервлета": зарегистрируйте bean-обработчики в контексте и создайте эмулятор mockMvc (Примечание: возможны две конфигурации: standaloneSetup или webAppContextSetup; обратитесь к документации), Конструктор по праву реализует шаблон "Построитель", поэтому вы можете объединить команды конфигурации для обработчиков исключений и обработчиков перед вызовом build().
@Before
public void setUp() {
final StaticApplicationContext appContext = new StaticApplicationContext();
appContext.registerBeanDefinition("BusinessExceptionHandler",
new RootBeanDefinition(BusinessExceptionHandler.class, null, null));
//InternalExceptionHandler extends ResponseEntityExceptionHandler to //handle Spring internally throwned exception
appContext.registerBeanDefinition("InternalExceptionHandler",
new RootBeanDefinition(InternalExceptionHandler.class, null,
null));
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.standaloneSetup(controller)
.setHandlerExceptionResolvers(getExceptionResolver(appContext))
.build();
coorg = OrganisationFixture.getFixture("orgID", "name", "webSiteUrl");
}
....
Получить средство разрешения исключений
private ExceptionHandlerExceptionResolver getExceptionResolver(
StaticApplicationContext context) {
ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
resolver.getMessageConverters().add(
new MappingJackson2HttpMessageConverter());
resolver.setApplicationContext(context);
resolver.afterPropertiesSet();
return resolver;
}
Запустите свои тесты
@Test
public void testGetSingleOrganisationRecordAnd404() throws Exception {
System.out.println("testGetSingleOrganisationRecordAndSuccess");
String request = "/orgs/{id}";
log.info("Request URL: " + request);
when(service.getOrganisation(anyString())).
thenReturn(coorg);
this.mvc.perform(get(request)
.accept("application/json")
.andExpect(content().contentType(
.APPLICATION_JSON))
.andExpect(status().notFound())
.andDo(print());
}
....
}
Надеюсь, это поможет.
Джейк.
Ответ 5
Это лучше:
((HandlerExceptionResolverComposite) wac.getBean("handlerExceptionResolver")).getExceptionResolvers().get(0)
И не забудьте просканировать @ControllerAdvice beans в классе @Configuration:
@ComponentScan(basePackages = {"com.company.exception"})
... проверено на Spring 4.0.2.RELEASE
Ответ 6
Попробуйте,
@RunWith(value = SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = { MVCConfig.class, CoreConfig.class,
PopulaterConfiguration.class })
public class ExceptionControllerTest {
private MockMvc mockMvc;
@Mock
private StatusController statusController;
@Autowired
private WebApplicationContext wac;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void checkUnexpectedExceptionsAreCaughtAndStatusCode500IsReturnedInResponse() throws Exception {
when(statusController.checkHealth()).thenThrow(new RuntimeException("Unexpected Exception"));
mockMvc.perform(get("/api/status"))
.andDo(print())
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$.error").value("Unexpected Exception"));
}
}