JavaRush /Курси /Модуль 5. Spring /Лекція 139: Практика: тестування контролерів за допомогою...

Лекція 139: Практика: тестування контролерів за допомогою MockMvc

Модуль 5. Spring
Рівень 9 , Лекція 8
Відкрита

Якщо ти колись хотів побачити, як твій 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.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ