Якщо ти колись хотів побачити, як твій 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‑запит на endpoint.
.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. Завжди перевіряй, що вимагає твій endpoint.
Що далі?
Тестування з MockMvc — це лише один етап у ланцюжку забезпечення якості. У реальних проєктах ти поєднуватимеш Unit, інтеграційні та функціональні тести, щоб мінімізувати баги і мати спокій. Далі в курсі ми заглибимося в інтеграційне тестування з MockMvc і підключення реальних баз через Testcontainers.
Для детальнішого занурення можеш ознайомитися з офіційною документацією MockMvc.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ