Перейти к основному содержимому

Тестирование исключений с помощью Spring MockMvc

· 3 мин. чтения

1. Обзор

В этой короткой статье мы увидим, как должны создаваться исключения в наших контроллерах и как тестировать эти исключения с помощью Spring MockMvc.

2. Генерация исключений в контроллерах

Давайте начнем изучать , как запустить исключение из контроллера .

Мы можем думать о службах, которые мы предоставляем из контроллера, так же, как если бы они были обычными функциями Java:

@GetMapping("/exception/throw")
public void getException() throws Exception {
throw new Exception("error");
}

Теперь давайте посмотрим, что происходит, когда мы вызываем эту службу. Во-первых, мы заметим, что код ответа службы — 500, что означает внутреннюю ошибку сервера.

Во-вторых, мы получаем тело ответа, подобное этому:

{
"timestamp": 1592074599854,
"status": 500,
"error": "Internal Server Error",
"message": "No message available",
"trace": "java.lang.Exception
at com.foreach.controllers.ExceptionController.getException(ExceptionController.java:26)
..."
}

В заключение, когда мы выбрасываем исключение из RestController , ответ службы автоматически сопоставляется с кодом ответа 500, а трассировка стека исключения включается в тело ответа.

3. Сопоставление исключений с кодами ответов HTTP

Теперь мы собираемся узнать , как сопоставить наши исключения с другими кодами ответов, отличными от 500.

Для этого мы собираемся создать собственные исключения и использовать аннотацию ResponseStatus , предоставленную Spring. Давайте создадим эти пользовательские исключения:

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadArgumentsException extends RuntimeException {

public BadArgumentsException(String message) {
super(message);
}
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class InternalException extends RuntimeException {

public InternalException(String message) {
super(message);
}
}
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {

public ResourceNotFoundException(String message) {
super(message);
}
}

Второй и последний шаг — создать простую службу в нашем контроллере, чтобы генерировать эти исключения:

@GetMapping("/exception/{exception_id}")
public void getSpecificException(@PathVariable("exception_id") String pException) {
if("not_found".equals(pException)) {
throw new ResourceNotFoundException("resource not found");
}
else if("bad_arguments".equals(pException)) {
throw new BadArgumentsException("bad arguments");
}
else {
throw new InternalException("internal error");
}
}

Теперь давайте посмотрим на разные ответы службы для разных исключений, которые мы сопоставили:

  • Для not_found мы получаем код ответа 404.
  • Учитывая значение bad_arguments , мы получаем код ответа 400
  • Для любого другого значения мы все равно получим 500 в качестве кода ответа.

Помимо кодов ответа, мы получим тело в том же формате, что и тело ответа, полученное в предыдущем разделе.

4. Тестирование наших контроллеров

Наконец, мы увидим, как проверить, что наш контроллер генерирует правильные исключения .

Первый шаг — создать тестовый класс и создать экземпляр MockMvc :

@Autowired
private MockMvc mvc;

Далее давайте создадим тестовые примеры для каждого из значений, которые может получить наш сервис:

@Test
public void givenNotFound_whenGetSpecificException_thenNotFoundCode() throws Exception {
String exceptionParam = "not_found";

mvc.perform(get("/exception/{exception_id}", exceptionParam)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNotFound())
.andExpect(result -> assertTrue(result.getResolvedException() instanceof ResourceNotFoundException))
.andExpect(result -> assertEquals("resource not found", result.getResolvedException().getMessage()));
}

@Test
public void givenBadArguments_whenGetSpecificException_thenBadRequest() throws Exception {
String exceptionParam = "bad_arguments";

mvc.perform(get("/exception/{exception_id}", exceptionParam)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isBadRequest())
.andExpect(result -> assertTrue(result.getResolvedException() instanceof BadArgumentsException))
.andExpect(result -> assertEquals("bad arguments", result.getResolvedException().getMessage()));
}

@Test
public void givenOther_whenGetSpecificException_thenInternalServerError() throws Exception {
String exceptionParam = "dummy";

mvc.perform(get("/exception/{exception_id}", exceptionParam)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isInternalServerError())
.andExpect(result -> assertTrue(result.getResolvedException() instanceof InternalException))
.andExpect(result -> assertEquals("internal error", result.getResolvedException().getMessage()));
}

С помощью этих тестов мы проверяем, что код ответа, тип возбуждаемого исключения и сообщения об этих исключениях являются ожидаемыми для каждого из значений.

5. Вывод

В этом руководстве мы узнали, как обрабатывать исключения в наших Spring RestController s и как проверить, что каждая открытая служба выдает ожидаемые исключения.

Как всегда, полный исходный код статьи доступен на GitHub .