Если вы когда-нибудь хотели увидеть, как ваш API живет своей лучшей жизнью, но при этом не хотите запускать весь сервер и прикладывать руку к Postman, — MockMvc спешит на помощь. Сегодня мы углубимся в практическое использование MockMvc для тестирования контроллеров в вашем Spring-приложении.
MockMvc — это инструмент для тестирования Spring MVC. Он позволяет вам запускать HTTP-запросы к вашим контроллерам без необходимости поднимать реальный сервер. Другими словами, MockMvc — это ваш карманный Postman, но гораздо более интегрированный и нацеленный на автоматизацию. Используя MockMvc, вы можете с лёгкостью имитировать запросы, проверять ответы контроллеров, тестировать сценарии с ошибками и ловить HTTP-статусы.
Подготовка окружения
Перед тем как приступить к тестированию с помощью MockMvc, убедитесь, что ваше приложение настроено для работы с тестами. Для этого в build-файле вашего проекта (например, pom.xml, если используется Maven) должны быть добавлены зависимости:
<dependencies>
<!-- Зависимость для Spring Boot Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Пример контроллера
Для тестирования мы будем использовать REST-контроллер, который обрабатывает сущность User. Вот как он может выглядеть:
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable("id") Long id) {
// Допустим, здесь вызывается сервис, который возвращает пользователя
User user = new User(id, "John Doe");
return ResponseEntity.ok(user);
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
// Имитация создания пользователя
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable("id") Long id) {
// Представим, что пользователь успешно удалён
return ResponseEntity.noContent().build();
}
}
Практика: тестируем контроллер с помощью MockMvc
Теперь займемся тестированием UserController. Тестировать будем основные методы: получение, создание и удаление пользователя.
Настройка MockMvc в тестах
В Spring Boot вы можете использовать аннотацию @WebMvcTest, чтобы поднять контекст только для тестирования веб-компонентов, таких как контроллеры.
@WebMvcTest(UserController.class) // Поднимаем контекст только для UserController
class UserControllerTest {
@Autowired
private MockMvc mockMvc; // MockMvc позволяет отправлять запросы.
@Test
void testGetUserById() throws Exception {
// Логика теста будет здесь
}
}
Тестирование GET-запроса
Начнем с основного: тестируем, что контроллер корректно отвечает на запрос получения пользователя.
@Test
void testGetUserById() throws Exception {
mockMvc.perform(get("/api/users/{id}", 1)) // Отправляем GET-запрос на эндпоинт.
.andExpect(status().isOk()) // Проверяем, что статус ответа — 200 OK.
.andExpect(content().contentType(MediaType.APPLICATION_JSON)) // Проверяем тип контента.
.andExpect(jsonPath("$.id").value(1)) // Проверяем, что поле "id" равно 1.
.andExpect(jsonPath("$.name").value("John Doe")); // Проверяем, что поле "name" равно "John Doe".
}
Здесь мы используем jsonPath, чтобы проверить содержимое JSON-ответа. Это невероятно удобно и позволяет работать даже с вложенными объектами.
Тестирование POST-запроса
Теперь добавим тест для метода создания пользователя.
@Test
void testCreateUser() throws Exception {
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"id\":1,\"name\":\"John Doe\"}")) // Отправляем POST-запрос с телом.
.andExpect(status().isCreated()) // Ожидаем статус 201 Created.
.andExpect(header().exists("Location")) // Проверяем, что есть заголовок Location.
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("John Doe"));
}
Здесь вы видите, как легко можно передать тело запроса, указывая JSON-данные. Также мы проверяем наличие заголовка Location, часто используемого в ответах на POST-запросы.
Тестирование DELETE-запроса
Тестируем удаление пользователя. Здесь важно убедиться, что контроллер возвращает статус 204 No Content.
@Test
void testDeleteUser() throws Exception {
mockMvc.perform(delete("/api/users/{id}", 1)) // Отправляем DELETE-запрос.
.andExpect(status().isNoContent()); // Проверяем, что статус ответа — 204 No Content.
}
Тестирование негативных сценариев
Не всегда ваши эндпоинты отвечают успешно. Иногда они бросают ошибки, и эти сценарии также важно тестировать.
Предположим, что метод getUserById может выбросить исключение, если пользователя не существует:
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable("id") Long id) {
if (id < 0) {
throw new UserNotFoundException("User not found");
}
User user = new User(id, "John Doe");
return ResponseEntity.ok(user);
}
Вот пример теста для этого сценария:
@Test
void testGetUserNotFound() throws Exception {
mockMvc.perform(get("/api/users/{id}", -1)) // Запрос с некорректным ID.
.andExpect(status().isNotFound()) // Ожидаем статус 404 Not Found.
.andExpect(jsonPath("$.error").value("User not found")); // Проверяем сообщение об ошибке.
}
Типичные ошибки и их устранение
Многие начинающие сталкиваются с проблемами при использовании MockMvc. Например, забывают указывать @WebMvcTest, и тогда тест начинает подтягивать зависимость от всей базы данных, сервисов и других компонентов приложения. Не забывайте настраивать MockMvc и изолировать тестируемые слои.
Еще одна распространенная ошибка — отсутствие параметров в запросах. К примеру, если вы забудете передать тело запроса в POST, MockMvc вернёт ошибку 400. Всегда пересматривайте, что требуется вашему эндпоинту.
Что дальше?
Тестирование с помощью MockMvc — это только один из этапов в цепочке обеспечения качества. В реальных проектах вы будете совмещать Unit, интеграционные и функциональные тесты для минимума багов и максимального спокойствия. Дальше в нашем курсе мы углубимся в интеграционное тестирование с MockMvc и подключение реальных баз данных через Testcontainers.
Для более детального погружения, вы можете ознакомиться с официальной документацией MockMvc.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ