JavaRush /Курсы /Модуль 5. Spring /Лекция 264: Интеграционное тестирование микросервисов с M...

Лекция 264: Интеграционное тестирование микросервисов с MockMvc

Модуль 5. Spring
27 уровень , 3 лекция
Открыта

Представим, что мы создаём микросервис, отвечающий за обработку заказов в онлайн-магазине. Он обрабатывает HTTP-запросы, вызывает сервисы для проверки наличия товаров на складе, взаимодействует с базой данных для сохранения информации о заказах. Всё это — набор взаимодействующих компонентов. И хотя вы протестировали их по отдельности в юнит-тестах, это ещё не гарантия, что всё работает вместе как надо. Именно тут вступает в игру интеграционное тестирование.

Интеграционное тестирование на уровне микросервиса позволяет:

  • Проверить взаимодействие между контроллерами, сервисами, репозиториями и другими компонентами.
  • Убедиться, что весь микросервис функционирует корректно, не потребляя реальные внешние зависимости.
  • Эмулировать реальные HTTP-запросы и проверить, как приложение обрабатывает их, возвращая ответы.

MockMvc: ключевой инструмент для интеграционного тестирования

Прежде чем мы займёмся тестированием, давайте разберём, что такое MockMvc.

MockMvc — это удобный инструмент из Spring Test, который позволяет:

  • Моделировать HTTP-запросы к вашему приложению без запуска реального web-сервера.
  • Проверять, как ваш контроллер обрабатывает запросы, возвращает ответы и работает с внутренними сервисами.
  • Симулировать реальные сценарии работы API, включая отправку параметров, тела запроса, настроек заголовков и многое другое.

Think about it like this: вместо того, чтобы собирать всю машину целиком и катать её по тестовой трассе, вы тестируете только её двигатель, но под нагрузкой, приближенной к реальной. Простыми словами: MockMvc позволяет протестировать веб-слой приложения изолированно, без необходимости поднимать весь сервер.

Почему MockMvc лучше для интеграционных тестов?

MockMvc предоставляет лёгкий способ тестирования:

  • Быстрее, чем запуск полноценных интеграционных тестов с поднятием сервера. Это экономит время, особенно в больших проектах.
  • Изолирует тестируемый слой. Если база данных или другие внешние системы не готовы, это не остановит тесты, ведь вы можете их замокать.
  • Гибкий API. MockMvc позволяет легко отправлять запросы и проверять, как ваши контроллеры обрабатывают их.

Подготовка к работе с MockMvc

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

Для начала убедимся, что в вашем pom.xml (или build.gradle) добавлены тестовые зависимости:


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

Для работы с MockMvc вам понадобится следующая настройка в тестируемом классе:


@SpringBootTest // Поднимает контекст Spring для тестов
@AutoConfigureMockMvc // Включает автоматическую настройку MockMvc

Легко, да? Теперь ваш тестовый класс готов к работе.


Практика: Тестирование REST API с использованием MockMvc

Хватит теории, пора показать, как всё это работает на практике!

Предположим, у нас есть микросервис для управления книгами, с REST API для работы с коллекцией книг.

Контроллер для тестирования

Вот пример контроллера, который предоставляет HTTP-методы для работы с книгами:


@RestController
@RequestMapping("/books")
public class BookController {

    private final BookService bookService;

    public BookController(BookService bookService) {
        this.bookService = bookService;
    }

    @GetMapping("/{id}")
    public ResponseEntity<Book> getBook(@PathVariable Long id) {
        return ResponseEntity.ok(bookService.findBookById(id));
    }

    @PostMapping
    public ResponseEntity<Book> createBook(@RequestBody Book book) {
        return ResponseEntity.status(HttpStatus.CREATED).body(bookService.saveBook(book));
    }
}

У нас есть два метода:

  1. GET /books/{id} — возвращает книгу по её идентификатору.
  2. POST /books — добавляет новую книгу.

Написание тестов для контроллера с MockMvc

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


@SpringBootTest
@AutoConfigureMockMvc
class BookControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private BookService bookService; // Мокаем зависимость сервиса

    @Test
    void testGetBook() throws Exception {
        // Данные для теста
        Book book = new Book(1L, "The Hobbit", "J.R.R. Tolkien");
        Mockito.when(bookService.findBookById(1L)).thenReturn(book);

        // Отправляем GET запрос и проверяем результат
        mockMvc.perform(get("/books/1"))
                .andExpect(status().isOk()) // Ожидаем статус 200 OK
                .andExpect(jsonPath("$.title").value("The Hobbit")) // Проверяем поле title
                .andExpect(jsonPath("$.author").value("J.R.R. Tolkien")); // Проверяем поле author
    }

    @Test
    void testCreateBook() throws Exception {
        // Данные для теста
        Book book = new Book(null, "1984", "George Orwell");
        Book savedBook = new Book(2L, "1984", "George Orwell");

        Mockito.when(bookService.saveBook(Mockito.any())).thenReturn(savedBook);

        // Отправляем POST запрос с JSON-данными
        String bookJson = """
                {
                  "title": "1984",
                  "author": "George Orwell"
                }
                """;

        mockMvc.perform(post("/books")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(bookJson))
                .andExpect(status().isCreated()) // Ожидаем статус 201 Created
                .andExpect(jsonPath("$.id").value(2L)) // Проверяем, что ID книги = 2
                .andExpect(jsonPath("$.title").value("1984")) // Проверяем название
                .andExpect(jsonPath("$.author").value("George Orwell")); // Проверяем автора
    }
}

Что здесь происходит?

  1. Аннотации:
    • @SpringBootTest поднимает весь контекст Spring.
    • @AutoConfigureMockMvc настраивает MockMvc автоматически.
    • @MockBean создаёт мок для зависимости BookService, который мы будем использовать для изоляции.
  2. Тесты:
    • В testGetBook мы мокаем метод findBookById в сервисе, чтобы он возвращал объект Book. Затем отправляем GET-запрос и проверяем, что ответ содержит правильные данные.
    • В testCreateBook мы мокаем метод saveBook, отправляем POST-запрос с JSON-данными и проверяем, что ответ содержит данные созданной книги.
  3. MockMvc API:
    • mockMvc.perform(...) выполняет HTTP-запрос.
    • andExpect(...) содержит все проверки результата, от статуса ответа до содержимого JSON.

Типичные ошибки при использовании MockMvc

Итак, на что стоит обратить внимание:

  • Не объявлена зависимость MockMvc или @AutoConfigureMockMvc. В таком случае тесты не смогут использовать MockMvc.
  • Ошибки сериализации JSON. Проверьте, подключён ли Jackson к вашему проекту.
  • Отсутствие моков для зависимостей. MockMvc тестирует только контроллер, так что обязательно мокаем сервисы через @MockBean.

MockMvc — невероятно мощный инструмент для тестирования взаимодействия с вашими контроллерами. Он позволяет на лету тестировать HTTP-запросы и мгновенно замечать ошибки, которые могли бы проявиться только в реальном использовании. С его помощью вы можете быть спокойны за корректность работы вашего API ещё до деплоя в продакшн.

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ