Какой у вас опыт написания интеграционных тестов?

Ответ

Интеграционные тесты проверяют взаимодействие между компонентами системы (БД, API, внешние сервисы). Основной стек: Spring Boot Test с аннотациями @SpringBootTest, @DataJpaTest, @WebMvcTest. Для изоляции зависимостей использую Mockito, для тестовых данных — H2 (in-memory) или Testcontainers (реальные контейнеризированные БД).

Почему это важно: Интеграционные тесты выявляют проблемы, которые unit-тесты не ловят (например, некорректные маппинги JPA, конфигурацию Spring, работу транзакций).

Пример интеграционного теста для REST API:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserControllerIntegrationTest {
    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void getUserById_ReturnsUserAndOkStatus() {
        ResponseEntity<UserDto> response = restTemplate
            .getForEntity("/api/users/1", UserDto.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody()).isNotNull();
        assertThat(response.getBody().getId()).isEqualTo(1L);
    }
}

Ключевые практики:

  1. Изоляция тестов: Использую @Transactional или @DirtiesContext для очистки состояния между тестами.
  2. Тестовая БД: Для тестов с JPA применяю @DataJpaTest с H2, для продакшен-подобного окружения — Testcontainers с PostgreSQL.
  3. Слои тестирования: Тестирую не только контроллеры, но и сервисы с репозиториями, чтобы локализовать проблему.

Ответ 18+ 🔞

А, интеграционные тесты, говоришь? Ну это ж, блядь, самое интересное место, где вся архитектура твоя, такая умная, на поверку оказывается пиздопроебибной конструкцией из палок и соплей!

Смотри, в чём прикол. Ты там наюнитил свои сервисы, всё замокал, они у тебя летают как фанера над Парижем. А потом запускаешь это всё вместе, и тут, сука, начинается ёперный театр! JPA-маппинг, который ты неделю вылизывал, оказывается, нихуя не работает, потому что забыл @OneToMany(mappedBy = "..."). Или транзакции, блядь, ведут себя как последние шлюхи — откатываются там, где не надо, и коммитят там, где всё должно было сломаться.

Основной инструмент — это @SpringBootTest. Он, сука, поднимает почти весь контекст, как на продакшене. Ты ему говоришь: «Эй, дружок, дай-ка мне TestRestTemplate», а он тебе: «На, хули, проверяй свои контроллеры на вшивость».

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

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserControllerIntegrationTest {
    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void getUserById_ReturnsUserAndOkStatus() {
        ResponseEntity<UserDto> response = restTemplate
            .getForEntity("/api/users/1", UserDto.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody()).isNotNull();
        assertThat(response.getBody().getId()).isEqualTo(1L);
    }
}

Выглядит-то просто, да? А теперь представь, что этот тест падает с 500 Internal Server Error. И начинается, блядь, детектив: то ли репозиторий хуйню отдаёт, то ли сервис словил NullPointer, то ли в контроллере маппинг кривой. Вот для этого они и нужны, эти тесты — чтобы выявить, где именно у тебя в цепочке сидит тот самый пидарас шерстяной, который всё ломает.

А теперь, блядь, главные правила, чтобы не охуеть от результатов:

  1. Изоляция — наше всё. Один тест насрал в базу, другой пришёл — а там уже всё засрано. Красота? Нет, пиздец. Поэтому либо @Transactional в помощь (после теста всё откатывается, как будто ничего и не было), либо @DirtiesContext (перезагружаем весь контекст, медленно, но верно). Выбирай, в каком виде тебе больше нравится есть говно.

  2. База тестовая — голова спокойная. Для быстрых проверок JPA-маппингов и запросов — @DataJpaTest и H2. Летит быстро, как хуй с горы. Но если хочешь почувствовать настоящую боль, как на проде (а ты должен её хотеть), то бери Testcontainers. Он поднимет тебе в докере реальную PostgreSQL, и ты узнаешь, что той красивой фичи, которая работала на H2, в постгресе не существует. И да, это спасёт тебе жопу перед выкаткой.

  3. Бей по всем слоям. Не зацикливайся только на контроллерах. Отдельно протестируй связку «Сервис -> Репозиторий». Уверен, что твой кастомный @Query с LEFT JOIN FETCH возвращает ровно то, что надо? А вот нихуя! Интеграционный тест репозитория это быстро покажет. Так ты локализуешь проблему: не «всё падает», а «падает вот в этом конкретном методе репозитория, иди сюда и чини».

Короче, смысл в чём. Юнит-тесты — это проверка, что каждый винтик в твоём механизме вроде как крутится. А интеграционные — это когда ты собираешь весь этот механизм, пытаешься его завести, а он, блядь, взрывается у тебя в руках. И это овердохуища важный этап, чтобы не получить такой же взрыв на проде.