Представьте ситуацию: все компоненты по отдельности проходят тесты, но сценарий через их границу завершается ошибкой. Какой механизм объясняет такой результат?
Такой результат объясняется дефектом взаимодействия компонентов, который модульные тесты намеренно не проверяют. Интеграционные тесты выявляют несовместимость контрактов, преобразований данных, настроек или порядка обмена между уже объединёнными частями системы.
Разделение тестирования на уровни появилось как способ управлять сложностью проверки программных систем. Полная проверка продукта после сборки слишком поздно обнаруживает источник ошибки, а проверка каждого компонента отдельно не показывает, корректно ли они работают совместно.
Поэтому тестирование организуют слоями: от изолированной проверки небольшой части до проверки взаимодействия компонентов и поведения всей системы. Каждый уровень снижает свой класс рисков, но не заменяет остальные.
При модульном тестировании компонент обычно окружён заменителями зависимостей или заранее подготовленными данными. Это позволяет точно проверить его собственную логику, однако реальные форматы сообщений, сетевые настройки, транзакции, сериализацию и порядок вызовов могут оставаться непроверенными.
Если один компонент ожидает дату в одном формате, а другой передаёт её в другом, оба могут пройти свои модульные тесты. Ошибка проявится только на границе взаимодействия, а её последствиями могут стать отказ сценария, потеря данных или некорректное состояние системы.
Интеграционный тест соединяет два или больше компонентов и проверяет их фактическое взаимодействие. Он может контролировать соответствие контракту, структуру и типы данных, коды ошибок, преобразования, состояние общей базы, транзакционные границы, тайм-ауты и зависимость от порядка операций.
Причина расхождения проста: модульные тесты отвечают на вопрос «правильно ли компонент работает в заданном окружении», а интеграционные — «совместимы ли компоненты в реальном взаимодействии». Даже идеальная логика каждого участника не гарантирует совместимость их предположений.
Набор интеграционных проверок не обязан повторять все модульные тесты. Его задача — покрыть значимые точки соединения и типичные отказные сценарии. При этом такие тесты обычно сложнее диагностировать, медленнее выполнять и сильнее зависят от окружения, поэтому избыточное их количество повышает стоимость сопровождения.
Практичный баланс — проверять модульные правила на уровне компонентов, а на интеграционном уровне оставлять контракты, критичные цепочки обмена, преобразования данных и взаимодействие с реальными инфраструктурными зависимостями. Если взаимодействие регулируется формальным контрактом, полезны также контрактные тесты, но они не заменяют проверку полного сценария с состоянием и инфраструктурой.
Сервис оформления заказа и сервис оплаты по отдельности проходят тесты. После объединения часть заказов получает отказ: один сервис передаёт сумму в минимальных денежных единицах, а другой интерпретирует её как сумму в основных единицах.
Рассматривались три варианта. Увеличить только модульные тесты было бы дёшево, но они продолжили бы использовать изолированные заглушки. Проверять весь пользовательский сценарий через интерфейс можно, однако такие тесты медленнее и хуже локализуют причину. Добавить интеграционную проверку реального контракта между сервисами точнее для данного риска, хотя она требует управляемого тестового окружения.
Выбрали третий вариант, дополнив его явной проверкой единиц измерения и граничных значений суммы. Это позволило обнаруживать несовместимость на границе сервисов до запуска полного набора сквозных тестов и сохранить модульные проверки для внутренней логики каждого сервиса.
Нет. Общий мок может воспроизводить согласованное, но нереалистичное поведение и скрывать несовместимость настоящих компонентов. Такой тест проверяет взаимодействие с моделью зависимости, но не обязательно реальный контракт; степень доверия определяется тем, насколько точно мок отражает фактическую зависимость.
Проблема может зависеть от времени, порядка сообщений, конкурентного доступа, состояния базы или нестабильной внешней зависимости. Модульные тесты обычно фиксируют окружение и порядок, поэтому не раскрывают такие условия. Для расследования нужны воспроизводимые данные, контроль состояния окружения и отдельные проверки таймингов или конкурентных сценариев, если именно они относятся к риску.
Не всегда. Если причина — чистое правило преобразования внутри одного компонента, её полезно закрепить модульным тестом. Если дефект вызван несовместимыми контрактами, транзакциями или особенностями инфраструктуры, модульная проверка одного участника не гарантирует защиту от повторения; нужен интеграционный тест, а иногда и контрактная проверка.