Инженерия ПО

Integration и E2E Testing

В 2012 году Knight Capital Group потеряла 440 млн долларов за 45 минут из-за бага в деплое. Код тестировался, но не было E2E-теста сценария 'что происходит, если включить старый флаг на новой версии'. За 45 минут автоматическая торговля выставила миллионы невыгодных ордеров. Правильно выстроенная пирамида тестирования с integration-тестом этого сценария предотвратила бы катастрофу.

  • **CI/CD gates:** GitHub Actions / GitLab CI запускают integration и E2E тесты как обязательный шаг перед merge; pull request не сливается, пока тесты не зеленые
  • **Playwright в крупных компаниях:** Microsoft, Google, Vercel используют Playwright для тестирования веб-приложений; Playwright создан командой Microsoft на основе опыта Puppeteer
  • **Pact Broker в микросервисах:** Netflix, Atlassian используют contract testing для управления совместимостью десятков сервисов без необходимости запускать все вместе

Пирамида тестирования

Пирамида тестирования - это не просто красивая схема: это экономическая модель. Юнит-тесты быстрые и дешевые, E2E-тесты медленные и дорогие. Команды, которые перевернули пирамиду (много E2E, мало юнитов), получают CI/CD-пайплайны по 40 минут и нестабильные тесты, которые падают по случайным причинам. Это называется "Test Ice Cream Cone" - антипаттерн, убивающий скорость разработки.

Классическая пирамида (Mike Cohn, 2009): Unit (70%) -> Integration (20%) -> E2E (10%). Современная интерпретация: разные продукты требуют разных пропорций. SaaS с UI: больше E2E. Backend API: больше integration. Библиотека: почти 100% unit. Главное правило - чем выше в пирамиде, тем дороже: написание, запуск, поддержка.

Проект имеет 5% unit-тестов, 5% integration-тестов и 90% E2E-тестов. Какая проблема возникнет первой?

Contract Testing

Сервис A вызывает сервис B. Как убедиться, что они понимают друг друга? E2E тест запустит оба - медленно, дорого, хрупко. Consumer-driven contract testing решает это изящнее: consumer (A) записывает контракт (что он ожидает от B), provider (B) проверяет, что он выполняет контракт. Оба теста запускаются независимо и быстро. Pact - главный инструмент для этого.

Pact workflow: 1) Consumer пишет тест с ожидаемым API, Pact записывает interaction в pact-файл. 2) Pact-файл публикуется в Pact Broker. 3) Provider загружает pact-файл и верифицирует его против реального API. 4) Если верификация прошла - можно деплоить. Can-I-Deploy - команда для проверки совместимости версий в Pact Broker.

В чём принципиальное отличие Consumer-Driven Contract Testing от обычного интеграционного теста?

E2E Testing с Playwright

E2E тесты проверяют систему глазами пользователя - от нажатия кнопки до записи в базе данных. Playwright изменил подход к E2E-тестированию: auto-wait вместо sleep, встроенная поддержка нескольких браузеров, трейсы для дебага, и главное - тесты работают в изолированных браузерных контекстах параллельно. Времена медленных Selenium-тестов остались в прошлом.

Playwright vs Cypress: Playwright поддерживает Chrome, Firefox, Safari из коробки; Cypress ограничен Chromium. Playwright работает вне браузера (Node.js), что позволяет полноценный async/await. Playwright Network interception позволяет мокировать API в E2E-тестах. Page Object Model (POM) - паттерн инкапсуляции UI-взаимодействий в классах.

E2E тест нажимает кнопку 'Submit' и сразу проверяет наличие success-сообщения, но тест нестабилен. Какое решение правильное?

Test Fixtures и изоляция данных

Тесты, которые зависят от порядка выполнения или остатков данных от других тестов - это тайные мины. Fixtures решают проблему изоляции: каждый тест получает чистое, предсказуемое состояние. В Playwright fixtures расширяют механизм зависимостей, в Jest - beforeEach/afterEach. Фабрики тестовых данных лучше хардкода: если схема меняется, менять нужно в одном месте.

Database isolation strategies: TRUNCATE (быстро, но опасно в prod-like окружении), transactions with rollback (для unit/integration), test databases (отдельная БД на каждый worker). Factory libraries: factory-girl/fishery (TypeScript), @faker-js/faker для генерации данных. Playwright fixtures: встроенные (page, browser, context) и кастомные (loginPage, apiClient).

100% покрытие кода тестами означает, что баги отсутствуют

Покрытие показывает, что код выполнялся, но не что он работает правильно

Тест может вызвать каждую строку кода и при этом не проверять результат (assertion-free test). Важнее качество assertions и тестируемые сценарии, чем процент coverage. Mutation testing (например, Stryker) - более честный способ оценить качество тестов.

Интеграционный тест создает запись в базе в beforeEach и НЕ удаляет её в afterEach. Тесты проходят по отдельности, но падают при запуске вместе. Почему?

Ключевые идеи

  • **Пирамида тестирования** - экономическая модель: unit дешевые и быстрые, E2E дорогие; перевернутая пирамида убивает скорость разработки
  • **Contract testing** - consumer и provider тестируются независимо через pact-файл; устраняет необходимость запускать оба сервиса одновременно
  • **Playwright auto-wait** - устраняет flaky tests; Page Object Model инкапсулирует UI-взаимодействия для повторного использования
  • **Изоляция данных** - каждый тест получает чистое состояние; фабрики тестовых данных vs хардкод

Связанные темы

Integration и E2E тестирование строятся на фундаменте подходов к разработке:

  • TDD и BDD — TDD определяет когда писать тесты; Integration/E2E тесты часто пишутся в BDD-стиле (Given/When/Then)
  • CI/CD — Integration и E2E тесты являются ключевыми gates в CI/CD pipeline

Вопросы для размышления

  • Как определить правильное соотношение unit/integration/E2E тестов для конкретного проекта? Какие факторы влияют на это решение?
  • Contract testing решает проблему совместимости сервисов. Какие проблемы он НЕ решает и требует дополнительных инструментов?
  • Если E2E тест нестабилен (падает в 10% запусков без изменения кода), как диагностировать причину и стоит ли добавлять retry?

Связанные уроки

  • stat-05-hypothesis
Integration и E2E Testing

0

1

Войти