Чем модульное тестирование отличается от интеграционного?

«Чем модульное тестирование отличается от интеграционного?» — вопрос из категории Основы тестирования, который задают на 10% собеседований QA Тестировщик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Модульное (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  # Проверка бизнес-логики, применённой при создании

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