В чем разница между unit, модульным и интеграционным тестированием?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Unit vs Модульное vs Интеграционное тестирование
Эти три типа тестирования образуют пирамиду тестов (Test Pyramid) и работают на разных уровнях абстракции приложения.
Unit тестирование
Unit тест — это тест отдельной функции, метода или класса в изоляции. Проверяет одну единицу функциональности.
Характеристики:
- Тестирует одну функцию/метод
- Использует моки для зависимостей
- Быстрое выполнение (миллисекунды)
- Должно быть большое количество
import pytest
from unittest.mock import Mock
class Calculator:
def __init__(self, logger):
self.logger = logger
def add(self, a: int, b: int) -> int:
result = a + b
self.logger.log(f"Added {a} + {b} = {result}")
return result
def test_calculator_add():
# Мокируем зависимость
mock_logger = Mock()
calc = Calculator(mock_logger)
# Тестируем только функцию add
result = calc.add(2, 3)
assert result == 5
mock_logger.log.assert_called_once()
Модульное тестирование (Module testing)
Модульный тест проверяет группу связанных функций или классов как целое. Это промежуточный уровень между unit и интеграционными тестами.
Характеристики:
- Тестирует несколько взаимосвязанных функций
- Может использовать реальные объекты (не всегда моки)
- Проверяет взаимодействие компонентов в модуле
- Медленнее unit тестов, но быстрее интеграционных
class UserRepository:
def __init__(self, db):
self.db = db
def create(self, name: str, email: str) -> int:
return self.db.insert("users", {"name": name, "email": email})
def get_by_email(self, email: str) -> dict:
return self.db.query("SELECT * FROM users WHERE email = %s", email)
class UserService:
def __init__(self, repository):
self.repository = repository
def register_user(self, name: str, email: str) -> int:
existing = self.repository.get_by_email(email)
if existing:
raise ValueError("User already exists")
return self.repository.create(name, email)
def test_user_registration():
# Используем реальный репозиторий, но с mock БД
mock_db = Mock()
mock_db.query.return_value = None # Пользователь не существует
mock_db.insert.return_value = 1
repo = UserRepository(mock_db)
service = UserService(repo)
# Проверяем взаимодействие UserService и UserRepository
user_id = service.register_user("John", "john@example.com")
assert user_id == 1
mock_db.insert.assert_called_once()
Интеграционное тестирование
Интеграционный тест проверяет, как разные модули/компоненты работают вместе. Использует реальные зависимости (БД, API, файловая система).
Характеристики:
- Тестирует несколько модулей вместе
- Использует реальные зависимости (БД, сеть)
- Медленное выполнение (секунды)
- Должно быть меньше, чем unit тестов
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# Используем реальную БД (обычно тестовую)
engine = create_engine("sqlite:///:memory:")
Session = sessionmaker(bind=engine)
def test_user_registration_integration():
# Реальная БД
db = Session()
repo = UserRepository(db)
service = UserService(repo)
# Проверяем полный процесс
user_id = service.register_user("John", "john@example.com")
assert user_id > 0
# Проверяем, что данные реально в БД
user = repo.get_by_email("john@example.com")
assert user["name"] == "John"
Пирамида тестов
/\
/ \ E2E / API тесты (мало, медленно)
/----\
/ \ Интеграционные тесты (среднее количество)
/--------\
/ \ Unit тесты (много, быстро)
/____________\
Сравнительная таблица
| Критерий | Unit | Модульное | Интеграционное |
|---|---|---|---|
| Область тестирования | Одна функция | Несколько функций | Несколько модулей |
| Зависимости | Моки | Моки/реальные | Реальные |
| Скорость | Быстро (мс) | Средне (мс-сек) | Медленно (сек) |
| Сложность | Простая | Средняя | Сложная |
| Количество | Много (70-80%) | Среднее (10-15%) | Мало (10-15%) |
| Изоляция | Полная | Частичная | Минимальная |
| Отладка | Легко | Средне | Сложно |
Пример с разными уровнями
class PaymentService:
def __init__(self, gateway, db):
self.gateway = gateway # Платёжный шлюз (Stripe, PayPal)
self.db = db
def process_payment(self, user_id: int, amount: float) -> bool:
transaction_id = self.gateway.charge(amount)
self.db.save_transaction(user_id, transaction_id, amount)
return True
# Unit тест - мокируем всё
def test_process_payment_unit():
mock_gateway = Mock()
mock_gateway.charge.return_value = "txn_123"
mock_db = Mock()
service = PaymentService(mock_gateway, mock_db)
result = service.process_payment(1, 100.0)
assert result is True
mock_gateway.charge.assert_called_once_with(100.0)
# Интеграционный тест - реальная БД и мок шлюза
def test_process_payment_integration():
real_db = Session() # Реальная БД
mock_gateway = Mock()
mock_gateway.charge.return_value = "txn_123"
service = PaymentService(mock_gateway, real_db)
result = service.process_payment(1, 100.0)
assert result is True
# Проверяем, что транзакция реально в БД
transaction = real_db.query(Transaction).filter_by(
id="txn_123"
).first()
assert transaction is not None
Когда использовать каждый тип
Unit тесты:
- Для критической бизнес-логики
- Для функций с множеством путей выполнения
- Для функций, которые сложно тестировать интеграционно
Модульные тесты:
- Для проверки взаимодействия компонентов модуля
- Для сложной логики, объединяющей несколько функций
Интеграционные тесты:
- Для API endpoints
- Для проверки работы с БД
- Для workflow, задействующих несколько сервисов
Рекомендация: соблюдайте пирамиду тестов — много unit тестов, среднее количество модульных, мало интеграционных.