Что такое E2E-тесты и почему они важны при разработке с кодинг-агентами
E2E-тест (end-to-end) проверяет приложение так, как его видит человек: агент с доступом к браузеру открывает страницу, кликает по кнопке, переходит на следующий экран и сообщает, если после действия возникла ошибка, ничего не произошло или ожидаемый результат не наступил. Тест смотрит на экран, решает, куда нажать, и проверяет, что оказалось дальше.
При разработке с кодинг-агентами вроде Claude Code или Codex такой тест закрывает конкретный разрыв. Агенты пишут код быстро и в основном правильно, но ошибаются неизбежно: упускают часть информации или выдают запутанную конструкцию с неожиданными побочными эффектами. Синтаксис при этом чистый, сборка проходит, а пользователь на третьем клике получает пустой экран и уходит. Разбор подхода к тестированию приложений, собранных с Claude Code описывает именно эту логику: агент проверяет поведение, а не внутреннюю реализацию.
Ключевое отличие E2E от остальных проверок: тест не зависит от того, какой код вы запустили или собрали. Он работает с самим приложением, с конечным продуктом, который видит пользователь. Отсюда и главная польза: проблемы обнаруживаются в той форме, в какой их переживают люди, и появляется время исправить их до того, как пользователи найдут их первыми.
Чем E2E-тест отличается от юнит-тестов и проверок кода
Разница проходит по уровню, на котором тест работает. Юнит-тест дергает отдельную функцию и сравнивает результат с ожидаемым значением. E2E запускает приложение целиком и проходит пользовательский путь: вход, заполнение формы, сохранение, проверка, что данные появились в списке.
| Уровень | Что проверяет | Когда гоняют | Что пропускает |
|---|---|---|---|
| Юнит-тест | Отдельную функцию или модуль в изоляции | На каждый коммит, секунды | Как части собираются вместе и как ведёт себя интерфейс |
| Интеграционный тест | Связку модулей, API и базы данных | При изменении контрактов | Реальный путь пользователя через экраны |
| E2E-тест | Сквозной сценарий в запущенном приложении | Перед мержем и по расписанию, минуты | Не локализует причину сбоя, дороже в поддержке |
Вывод из таблицы простой: E2E дороже и медленнее юнит-тестов, поэтому им не заменяют нижние уровни, а дополняют. Юнит-тест скажет, что сломалась функция расчёта скидки. E2E скажет, что после применения скидки из корзины исчез самовывоз. Для кодинг-агента ценнее второй сигнал: он проверяет результат работы агента, а не его намерения.
Хорошая связка выглядит так: агент пишет код и юнит-тесты к нему, человек или отдельный прогон проверяет критичные пользовательские сценарии в браузере. Про то, как выстроить приёмку кода агентов по проверкам, а не по обещаниям, подробно разобрано в материале про средовой подход к ИИ-агентам.
Почему кодинг-агенты хорошо пишут код, но всё равно ошибаются
Ошибки агентов редко выглядят как грубый синтаксис. Чаще это пропущенная деталь: не обработан пустой ответ API, не учтён пользователь без прав, кнопка отрисовывается, но обработчик не привязан. Такие дефекты не видны в диффе и почти не ловятся линтером, потому что формально код корректен.
Ещё один класс проблем: побочные эффекты. Агент правит одну ветку логики и задевает соседнюю, о которой не знал, потому что не держал в голове весь проект. В браузере это проявляется сразу: экран открывается, но данные не те, форма отправляется, но статус не меняется.
Проверка в браузере ловит именно поведение. Она не спрашивает, что агент имел в виду, и не сверяет реализацию с документацией. Она нажимает кнопку и смотрит на результат, как это сделает пользователь.
Как изменились E2E-тесты: от ручных скриптов к агенту с планом действий
Раньше E2E-тесты собирались руками. Нужно было пройти каждую страницу, найти ID кнопки, которую тест должен нажать, и записать ожидаемый результат действия на этой странице. Работа механическая, но объёмная: на десяток экранов уходил день, и это только первый проход.
Настоящая боль начиналась позже. Тесты требовали поддержки: менялся дизайн страницы или логика, и скрипты приходилось обновлять. Затраты на сопровождение растягивались на весь жизненный цикл теста, и часто набор из сотни сценариев тихо гнил, потому что переписывать его никто не хотел. Источник описывает эту эволюцию и переход к агентному подходу.
С кодинг-агентами картина изменилась: тесты стали быстрее собирать и проще поддерживать. Современный E2E-тест по сути представляет собой агента с доступом к компьютеру или браузеру и планом того, что он должен сделать. Ему задают сценарий: добраться до страницы A, выполнить действие B, получить результат C.
Почему агентные тесты устойчивее к изменениям дизайна
Агента не нужно обновлять при изменениях дизайна страницы, пока нужная кнопка остаётся на ней. Он ищет кнопку по смыслу и тексту, а не по жёстко зашитому селектору. Ручной тест с ID элемента ломается от смены вёрстки, агентный проходит тот же путь на новом макете. Эта устойчивость и её границы разобраны в первоисточнике.
Границы важны. Если кнопка исчезла с экрана, переехала в скрытое меню или сценарий изменился по смыслу (раньше оплата шла в один клик, теперь через подтверждение), промпт придётся править. Поддержка не исчезла полностью, она сместилась: вы поддерживаете описание сценария, а не селекторы и ожидаемые строки.
Что агентные E2E-тесты находят лучше узких скриптов
Агентный тест обобщённее узкого скрипта и способен обнаружить проблемы, которые прежние проверки не находили. Причина в том, что скрипт сверяет только заранее прописанные условия, а агент смотрит на экран целиком.
- Ошибка после нажатия: появился экран 500, упал запрос, вылетело сообщение об исключении.
- Отсутствие реакции: клик прошёл, но ничего не изменилось и запрос не ушёл.
- Невыполнение ожидаемого действия: форма отправилась, а запись не создалась или создалась с пустыми полями.
- Побочный дефект: сценарий формально завершился, но на экране остались чужие данные или неверный статус.
Пример из практики поддержки больших наборов: в разборе аудита 2600 автотестов перед миграцией с Java на .NET видно, сколько мусора накапливается в наборе E2E-тестов без регулярной ревизии: около 450 неактуальных скриншотных тестов и удалённые дубли. Сценарии на естественном языке читать и ревизовать дешевле, чем код с селекторами.
Как описать E2E-тест промптом: сценарий, роли и ожидаемый результат
Промпт для E2E-теста состоит из четырёх частей: стартовая точка, последовательность действий, ожидаемый результат и критерий провала. Четвёртая часть самая недооценённая. Без неё агент может решить, что сценарий прошёл, хотя приложение вело себя не так, как задумано.
Доступ к браузеру дают через MCP-сервер, например Playwright MCP: он отдаёт агенту инструменты вроде открытия страницы, клика, ввода текста, чтения DOM и снятия скриншота. Конкретный транспорт вторичен, важен принцип: агент получает возможность управлять браузером и видеть результат. Первоисточник описывает подход в общем виде, без привязки к конкретному MCP-серверу, поэтому связку «агент + Playwright MCP» считайте практическим дополнением, а не цитатой.
Полезно сразу договориться, что агент делает при сбое: останавливается, фиксирует шаг, прикладывает скриншот и текст ошибки. Тогда результат прогона читается за минуту, а не превращается в расследование.
Как задавать роли: обычный пользователь и админ
Роль задаётся прямо в промпте как часть сценария: под каким пользователем стартуем, что ему доступно, что должно быть недоступно. Тест только от администратора маскирует баги прав доступа, потому что админ видит всё и не воспроизводит ограничения обычного аккаунта.
Рабочая структура описания роли:
- Кто именно действует: гость, обычный пользователь, администратор.
- С какого экрана стартует и в каком состоянии аккаунт (пустой профиль, есть заказы, активная подписка).
- Что роль может сделать и что должно быть заблокировано.
- Какое поведение системы считается правильным при попытке запрещённого действия: редирект, сообщение, скрытая кнопка.
Один сценарий удобно прогонять дважды, от обычного пользователя и от админа. Расхождения в поведении часто и оказываются багом: пользователь видит кнопку удаления, которая должна быть только у администратора, или админ получает отказ по правам.
Пример промпта для E2E-сценария
Шаблон ниже иллюстративный. Он показывает структуру, а не проверенный на конкретном проекте набор инструкций.
Цель: проверить сценарий оформления заказа.
Роль: обычный пользователь, тестовый аккаунт <email>, пароль <пароль>.
Старт: <URL стенда>, страница каталога.
Шаги:
1. Открыть карточку первого товара в списке.
2. Нажать «Добавить в корзину».
3. Перейти в корзину и нажать «Оформить заказ».
4. Заполнить обязательные поля и подтвердить заказ.
Ожидаемый результат: появился экран подтверждения с номером заказа,
заказ виден в разделе «Мои заказы» со статусом «Оплачен».
Считать провалом: ошибка после клика, отсутствие реакции на клик,
отсутствие заказа в списке, неверный статус, пустые поля в заказе.
При сбое: остановись, укажи номер шага, что ожидал и что увидел,
приложи скриншот.
Не делай: не удаляй заказы, не меняй пароль и настройки аккаунта.
Последний блок («не делай») экономит нервы. Агент без ограничений иногда «чинит» окружение по пути: удаляет мешающую запись или меняет настройки, чтобы сценарий прошёл. Запрет на разрушающие действия стоит в промпте по умолчанию.
Как запускать агентные E2E-тесты в CI/CD
Дальше начинается инженерия вокруг теста, и здесь готовых рецептов из первоисточника нет: он описывает сам подход, а не пайплайн. Рабочая схема, которая используется в типовых проектах, опирается на два режима прогона.
Первый режим: блокирующий прогон перед мержем. Второй: регулярный прогон по расписанию на dev и production. Они ловят разные классы проблем, поэтому нужны оба.
Прогон перед мержем: что блокировать
Блокировать мерж стоит только по критичным пользовательским сценариям: вход, оплата, создание основной сущности, ключевые права доступа. Полный набор агентных тестов на каждый pull request съест время команды и деньги на токены.
В GitHub Actions прогон выглядит привычно: джоб поднимает приложение и базу, запускает агента с промптом, складывает в artifacts скриншоты, видео и лог шагов. Артефакты важнее статуса: по ним видно, на каком шаге тест сломался и что именно было на экране.
С нестабильными тестами нужна дисциплина. Флейк изолируют, помечают и разбирают, но не отключают молча: тихо выключенный тест создаёт ощущение покрытия, которого нет. Компромисс между скоростью и надёжностью придётся выбирать осознанно, обычно в пользу короткого набора критичных сценариев и отдельного ночного прогона остального.
Прогон по расписанию в dev и production
Расписание ловит то, что не воспроизводится в чистом CI: миграции на реальных данных, истёкшие токены, изменившиеся ответы внешних API, проблемы окружения. На dev такие прогоны дешевле и безопаснее, туда можно включить больше сценариев.
На production набор ограничивают сценариями, которые ничего не разрушают: открыть страницу, войти тестовым аккаунтом, проверить, что ключевой флоу доступен. Никаких оплат реальными картами, удалений и рассылок. Тестовые аккаунты должны быть помечены, а создаваемые записи стоит регулярно чистить, иначе проверка сама станет источником мусорных данных.
Оба режима вместе дают нормальную картину: перед мержем отсекаются регрессии кода, по расписанию обнаруживаются деградации среды и данных, которые проявились без вашего участия.
Где запускать тесты: облачный сервер, GitHub Actions runner или отдельная машина
Агент с браузером требователен к окружению: нужен запущенный Chromium, стабильная сеть, доступ к стенду и достаточно памяти. Выбор площадки влияет на стабильность и на стоимость одного прогона.
| Вариант | Плюсы | Минусы | Когда подходит |
|---|---|---|---|
| GitHub Actions runner | Нулевая настройка, интеграция с PR, артефакты из коробки | Лимиты по времени и окружению, доступ во внутреннюю сеть требует self-hosted агента | Старт проекта, публичные стенды, короткие блокирующие прогоны |
| Облачный сервер (VM) | Полный контроль над образом, воспроизводимость, можно держать браузер и прокси | Нужно администрировать, платить за простой, следить за обновлениями | Регулярные прогоны по расписанию, доступ к приватным стендам |
| Отдельная машина | Максимум контроля, свои секреты и сеть, удобная отладка | Нет масштабирования, одна точка отказа, зависимость от чужого питания и сети | Небольшая команда, один-два сценария, закрытый контур |
Рамка выбора простая. Если прогонов мало и они короткие, хватит hosted runner. Если тесты идут по расписанию и нужен доступ в приватную сеть, берите VM или self-hosted runner. Если проект небольшой и всё упирается в закрытый контур, отдельная машина переживёт и это, но перестанет работать, как только понадобится параллельность.
Стоимость считайте по своей нагрузке: количество прогонов в сутки умножайте на длительность одного прогона и на расход модели. Агентный тест с просмотром скриншотов прожорливее обычного скрипта, поэтому ночной полный прогон часто выгоднее, чем запуск всего набора на каждый коммит.
Ограничения и подводные камни агентных E2E-тестов
Подход не бесплатный и не самоподдерживающийся. Прямые минусы, с которыми придётся жить:
- Недетерминированность. Один и тот же промпт может дать разное поведение: сегодня агент пошёл по одному пути, завтра по другому. Часть ложных срабатываний идёт отсюда.
- Стоимость. Каждый прогон расходует токены и время, а отладка сбоя иногда занимает больше, чем написание сценария.
- Флейки. Сеть, тайминги анимаций и медленные ответы API дают падения, не связанные с багами. Без изоляции такие тесты перестают замечать.
- Ложный зелёный. Агент может пройти сценарий другим путём и не увидеть проблему, которую искали. Спасают точные критерии провала в промпте.
- Поддержка по смыслу. Дизайн меняется, и тест выживает, а бизнес-логика меняется, и промпт нужно переписать.
Агентные E2E-тесты не заменяют юнит-тесты и код-ревью. Они закрывают верхний уровень: проверяют, что приложение работает для человека целиком. При этом проверка результата вообще стала дороже генерации кода, и это отдельная проблема, разобранная в статье про разработку с ИИ-агентами. Автотест в браузере сокращает эту нагрузку, но не отменяет её.
Практические меры, которые снижают риски: держать набор узким и критичным, формулировать критерии ошибки явно, складывать артефакты каждого прогона, разбирать флейки в течение недели, а не откладывать.
С чего начать: минимальный план запуска
Старт с одного сценария дешевле, чем попытка покрыть всё приложение сразу. Порядок шагов:
- Выберите один критичный путь, без которого продукт не работает. Не самый сложный, а самый важный.
- Подключите агенту доступ к браузеру через Playwright MCP или аналогичный сервер и убедитесь, что он открывает ваш стенд.
- Опишите сценарий промптом: роль, стартовая страница, шаги, ожидаемый результат, критерии провала, запрет на разрушающие действия.
- Прогоните вручную несколько раз. Если агент каждый раз идёт по-разному, уточните формулировки: неоднозначность в промпте превращается в флейк.
- Встройте прогон в CI перед мержем как блокирующий для этого одного сценария.
- Добавьте расписание: сначала на dev, затем на production с безопасным набором действий.
- Расширяйте набор только после того, как первый сценарий перестал падать без причины.
Дальше набор растёт по мере появления новых критичных путей, а промпты ревизуются вместе с изменениями логики. Такой цикл даёт то, ради чего всё затевается: баги ловятся до пользователей, а не в день релиза.