Ответ
Модульное (Unit) тестирование проверяет изолированно минимальную единицу кода (функцию, метод, класс). Интеграционное тестирование проверяет взаимодействие и совместную работу нескольких модулей или систем.
Ключевые различия:
| Критерий | Модульное тестирование | Интеграционное тестирование |
|---|---|---|
| Объект | Один класс или функция. | Группа классов, модулей, внешние системы (БД, API). |
| Изоляция | Полная. Зависимости заменяются тестовыми двойниками (Mock, Stub). | Минимальная. Используются реальные или приближённые к реальным зависимости. |
| Скорость | Очень быстрое (миллисекунды). | Медленное (секунды, минуты). |
| Цель | Проверить корректность логики единицы кода. | Проверить корректность взаимодействия и поток данных между компонентами. |
| Частота запуска | При каждом коммите (CI). | Реже (ночью, перед релизом). |
Пример модульного теста (с Mock):
# Тестируем сервис, изолировав его от реальной базы данных
from unittest.mock import Mock
def test_user_service_get_user():
# 1. Создаём мок репозитория
mock_repo = Mock()
mock_repo.find_by_id.return_value = User(id=1, name="Alice")
# 2. Внедряем мок в тестируемый сервис
service = UserService(mock_repo)
# 3. Вызываем метод и проверяем логику
result = service.get_user_profile(1)
# 4. Проверяем, что сервис корректно обработал данные от репозитория
assert result["name"] == "Alice"
# Проверяем взаимодействие: метод репозитория был вызван с правильным аргументом
mock_repo.find_by_id.assert_called_once_with(1)
Пример интеграционного теста:
# Тестируем взаимодействие сервиса с реальной (например, тестовой) БД
def test_user_creation_flow():
# 1. Поднимаем тестовую БД (Docker, in-memory)
db = start_test_database()
repo = UserRepository(db.connection)
service = UserService(repo)
# 2. Выполняем операцию, затрагивающую несколько слоёв
new_user = service.create_user("test@example.com", "password123")
# 3. Проверяем результат через прямое обращение к БД
user_from_db = repo.find_by_id(new_user.id)
# 4. Проверяем согласованность данных и состояние системы
assert user_from_db is not None
assert user_from_db.email == "test@example.com"
assert user_from_db.is_active is True # Проверка бизнес-логики, применённой при создании
Практическое правило: Пирамида тестов — много быстрых модульных тестов в основании, меньше интеграционных наверху.
Ответ 18+ 🔞
А, ну вот, опять про эти ваши тесты. Слушай, давай я тебе на пальцах объясню, как это всё работает, а то смотрю, ты уже глаза стеклянные делаешь.
Представь себе, блядь, завод по сборке табуреток. Модульный тест — это когда ты берёшь одну ножку, отдельно от всей табуретки, и проверяешь: ровная ли она, не шатается ли, в паз влезает. Всё, хуй с ней, с остальной табуреткой. Только ножка. Изолированно, блядь.
А интеграционный тест — это когда ты эту ножку, сиденье и три другие ножки собрал, хуяк-хуяк, и пытаешься на эту конструкцию сесть. Проверяешь, не развалится ли она под твоей жопой, и все ли детали друг друга понимают.
Вот и вся разница, ёпта. В одном случае ты проверяешь деталь, в другом — как эти детали друг другу не ебут мозг при совместной работе.
Ключевые различия, если по-простому:
| Критерий | Модульное тестирование | Интеграционное тестирование |
|---|---|---|
| Что проверяем | Одну функцию, один класс. Как будто он один во всей вселенной. | Куча классов вместе, а то и целую систему с базой данных или другим сервисом. |
| Изоляция | Полнейшая, блядь. Всех соседей и друзей подменяем муляжами (Mock, Stub). | Похуй на изоляцию. Тут уже почти как в бою: реальная база, реальные запросы. |
| Скорость | Быстрее, чем ты успеваешь моргнуть. | Медленнее, чем черепаха в сиропе. Иногда ждать овердохуища. |
| Цель | Убедиться, что эта маленькая функция не ебёт какую-то простую логику. | Убедиться, что когда одна часть системы плюёт, другая не бежит с тряпкой вытирать, а правильно понимает, что это плевок, а не дождь. |
| Когда запускаем | Постоянно, после каждой строчки кода, как параноики. | Реже, когда уже что-то собрали, чтобы не сойти с ума от ожидания. |
Вот смотри, как модульный тест выглядит (всё под контролем, все муляжи):
# Тестируем сервис, изолировав его от реальной базы данных
from unittest.mock import Mock
def test_user_service_get_user():
# 1. Делаем фейковый репозиторий, который всегда возвращает то, что мы скажем
mock_repo = Mock()
mock_repo.find_by_id.return_value = User(id=1, name="Alice")
# 2. Суём этот фейк в наш сервис вместо настоящего
service = UserService(mock_repo)
# 3. Дёргаем метод и смотрим, не обосрался ли он
result = service.get_user_profile(1)
# 4. Проверяем, что сервис не накосячил с данными, и что он дёрнул репозиторий как надо
assert result["name"] == "Alice"
mock_repo.find_by_id.assert_called_once_with(1)
А вот интеграционный тест (тут уже по-взрослому, почти продакшен):
# Тестируем, как всё это хозяйство дружит с реальной (тестовой) БД
def test_user_creation_flow():
# 1. Поднимаем тестовую базу (хоть в докере, хоть в памяти)
db = start_test_database()
repo = UserRepository(db.connection)
service = UserService(repo)
# 2. Выполняем целую цепочку: сервис -> репозиторий -> БД
new_user = service.create_user("test@example.com", "password123")
# 3. Лезем прямо в базу, проверяем, записалось ли всё
user_from_db = repo.find_by_id(new_user.id)
# 4. Смотрим, не насрали ли мы что-то в логике и все ли данные на месте
assert user_from_db is not None
assert user_from_db.email == "test@example.com"
assert user_from_db.is_active is True # А это бизнес-правило сработало?
И главное правило, чувак: Строй пирамиду. Внизу, в основании, — куча быстрых модульных тестов, которые ловят косяки в мелочах. А наверху, ближе к вершине, — несколько тяжёлых интеграционных, которые проверяют, что вся эта конструкция в целом не развалится нахуй.