Ответ
Unit-тесты проверяют изолированно одну единицу кода (класс, метод). Интеграционные тесты проверяют взаимодействие нескольких модулей или с внешними системами.
Сравнительная таблица:
| Характеристика | Unit-тест | Интеграционный тест |
|---|---|---|
| Объект тестирования | Один класс/метод (единица). | Группа классов, модуль, взаимодействие с БД, API. |
| Зависимости | Изолируются с помощью моков (Mockito). | Используются реальные или приближённые к реальным зависимости (testcontainers, in-memory БД). |
| Скорость | Очень быстрые (миллисекунды). | Медленные (секунды, минуты). |
| Цель | Проверить корректность логики единицы кода. | Проверить корректность взаимодействия и интеграции компонентов. |
| Частота запуска | Запускаются постоянно, при каждом изменении кода. | Запускаются реже (ночью, перед релизом). |
Пример Unit-теста (JUnit 5 + Mockito):
@Test
void shouldReturnUserWhenFound() {
// 1. ARRANGE: Создаём мок зависимости и тестируемый сервис
UserRepository mockRepo = mock(UserRepository.class);
UserService service = new UserService(mockRepo);
User expectedUser = new User(1L, "Alice");
when(mockRepo.findById(1L)).thenReturn(Optional.of(expectedUser));
// 2. ACT: Вызываем тестируемый метод
User actualUser = service.getUserById(1L);
// 3. ASSERT: Проверяем результат
assertEquals("Alice", actualUser.getName());
verify(mockRepo).findById(1L); // Проверяем взаимодействие с моком
}
Пример Интеграционного теста (Spring Boot Test):
@SpringBootTest
@AutoConfigureTestDatabase // Заменяет БД на embedded (H2)
class UserServiceIntegrationTest {
@Autowired
private UserRepository repository; // Реальный репозиторий
@Autowired
private UserService service; // Реальный сервис
@Test
void shouldSaveAndRetrieveUser() {
// Действия с реальной БД
User savedUser = repository.save(new User(null, "Bob"));
User foundUser = service.getUserById(savedUser.getId());
assertThat(foundUser.getName()).isEqualTo("Bob");
}
}
Пирамида тестирования: Unit-тесты составляют её основу (их больше всего), а интеграционные находятся уровнем выше (их меньше). Оба типа необходимы для качественного проекта.
Ответ 18+ 🔞
Давай я тебе на пальцах объясню, в чём разница между этими двумя типами тестов, а то я вижу, у тебя в глазах уже туман от этих умных словечек.
Представь себе, блядь, двигатель от машины. Unit-тест — это когда ты снял свечу зажигания, положил её на стол и проверяешь, даёт ли она искру. Одна деталь, в полной изоляции, нихуя вокруг. Интеграционный тест — это когда ты эту свечу вкрутил обратно в движок, завёл его и слушаешь, не троит ли он, блядь. Проверяешь, как эта сволочь работает уже в компании с другими железяками.
Короче, смотри таблицу, чтобы не ебать мозг:
| Что проверяем | Unit-тест | Интеграционный тест |
|---|---|---|
| Объект | Один класс или метод. Одиноко, как хуй в проруби. | Целая банда классов, работа с БД, API — полный ансамбль. |
| Зависимости | Всех заместо них подставляем муляжи (моки). Как будто подсадили актёров-дублёров. | Используем настоящих уродов: реальную БД (или почти реальную). |
| Скорость | Молниеносно. Тыкнул — получил результат. | Тяжело и долго. Как ждать, пока тёща в туалете соберётся. |
| Цель | Убедиться, что вот эта маленькая функция не ебёт мозг в логике. | Убедиться, что все эти функции, собравшись вместе, не устроят пиздец. |
| Когда запускаем | Постоянно, после каждой строчки кода. | Реже, но основательно — перед тем как выкатить всё нахуй. |
Вот тебе пример Unit-теста, где мы все вокруг наёбываем (JUnit 5 + Mockito):
@Test
void shouldReturnUserWhenFound() {
// 1. ПОДГОТОВКА: Создаём фейковый репозиторий и наш сервис
UserRepository mockRepo = mock(UserRepository.class);
UserService service = new UserService(mockRepo);
User expectedUser = new User(1L, "Alice");
// Говорим муляжу: "Когда вызовут findById с единичкой, верни эту Алису"
when(mockRepo.findById(1L)).thenReturn(Optional.of(expectedUser));
// 2. ДЕЙСТВИЕ: Дёргаем наш метод
User actualUser = service.getUserById(1L);
// 3. ПРОВЕРКА: А получили ли мы то, что хотели?
assertEquals("Alice", actualUser.getName());
verify(mockRepo).findById(1L); // И заодно тыкаем муляж носом: "А вызывали тебя, сука?"
}
А вот интеграционный тест, где уже по-взрослому, с настоящей БД (Spring Boot):
@SpringBootTest
@AutoConfigureTestDatabase // Подменяет продакшн-БД на лёгкую, in-memory (типа H2)
class UserServiceIntegrationTest {
@Autowired
private UserRepository repository; // Внедряем ПРЯМО НАСТОЯЩИЙ репозиторий, ёпта!
@Autowired
private UserService service; // И настоящий сервис
@Test
void shouldSaveAndRetrieveUser() {
// Сохраняем юзера в БД — это уже не муляж!
User savedUser = repository.save(new User(null, "Bob"));
// А теперь достаём его через сервис, который пойдёт в эту же БД
User foundUser = service.getUserById(savedUser.getId());
assertThat(foundUser.getName()).isEqualTo("Bob"); // И охуеваем, если не сработало
}
}
Пирамида тестирования — это вообще святое. Внизу, основанием, лежат unit-тесты, их дохуя. Над ними — интеграционные, их поменьше. А на самом верху — редкие и толстые, как начальник, end-to-end тесты. Без этой пирамиды твой проект — это как дом без фундамента, который развалится от первого же чиха в его сторону.