ХЕЛЛОУИН СЕЙЛ-10%
До 31 октября-10%
ХЕЛЛОУИН СЕЙЛ-10%
До 31 октября-10%
ХЕЛЛОУИН СЕЙЛ-10%
До 31 октября-10%
ХЕЛЛОУИН СЕЙЛ-10%
До 31 октября-10%
ХЕЛЛОУИН СЕЙЛ-10%
До 31 октября-10%
ХЕЛЛОУИН СЕЙЛ-10%
До 31 октября-10%

Пирамида тестирования в вайбкодинге

Иван Балашов из SOFTSWISS объясняет, как юнит-, интеграционные и E2E-тесты ловят поломки, которые AI-агент вносит в код, и что делать, когда тест упал.
Иван Балашов
Разбор лекции Ивана Балашова от 24 сентября 2026 года из библиотеки стримов Guide DAO.
Иван Балашов руководит разработкой в SOFTSWISS. В профессии он больше десяти лет, успел поработать в Ozon. Полтора года он активно пользуется LLM, а последний год почти не пишет код вручную. Слушатели его стримов про разработку с AI-агентами спросили, как сделать так, чтобы агенты ничего не ломали, и как за ними следить. Иван ответил циклом лекций о тестировании.
Тест фиксирует, как должно вести себя приложение. Агент меняет код, вы запускаете тесты, и упавший тест показывает, где поведение разошлось с ожидаемым. В прошлый раз Иван разбирал юнит-тесты, а на этом стриме добавил интеграционные и E2E-тесты и достроил пирамиду тестирования. В основании пирамиды лежат юнит-тесты, они проверяют отдельные функции. Уровнем выше идут интеграционные тесты, которые проверяют работу с базами данных и внешними сервисами. На вершине стоят E2E-тесты: они проходят приложение так, как им пользуется реальный человек. Чем выше уровень, тем тест тяжелее и дольше идёт.

Юнит-тесты проверяют функции

Юнит-тест проверяет кусок кода, обычно одну функцию, и не обязательно бизнес-логику. В одном из проектов Ивана к функции verifyContractResponse написаны два теста. Первый передаёт ей валидную подпись, и функция должна её принять. Второй подсовывает мусор, и функция должна его отбросить. Так же отдельно тестируют подсчёт хеша, проверку строки и всё остальное. Все тесты проекта запускаются одной командой в терминале.
Тесты Иван строит по схеме Arrange, Act, Assert: подготовить данные, выполнить действие, проверить, что функция отработала как нужно. В юнит-тесте всё, от чего зависит функция, заменяют моками. Мок изображает базу данных или внешний сервис и возвращает то, что задал автор теста.
Если старый код не готов к тестам, его можно подготовить вместе с агентом. На прошлом занятии Иван взял файл с логикой JWT, который написал давным-давно. В таком виде протестировать его было нельзя. С помощью Claude Иван переработал код так, чтобы функции можно было проверить, и написал к ним юнит-тесты.

Интеграционные тесты подключают настоящую базу

Проблемы обычно начинаются, когда функции собираются в рабочий кусок кода. Частая история: каждую функцию проверили юнит-тестом по отдельности, а вместе они не работают. Такие поломки ловит следующий уровень. Интеграционный тест проверяет все слои приложения сразу.
В Go-проекте Ивана есть сценарий, который очищает просроченные ключи. Ему нужны репозиторий, то есть база данных, ограничитель частоты запросов на Redis и сервис уведомлений. Сервис уведомлений может быть и сторонним, скажем, для рассылки SMS. В юнит-тесте все эти зависимости заменили бы моками. Интеграционный тест подключает настоящую базу, настоящий Redis и все интеграции, которые можно сделать настоящими, и проверяет, что они работают так, как от них ждут.
Цена такой проверки видна сразу. Перед запуском Иван включил Docker. Тест шёл почти пять секунд: нужно было поднять базу и Redis, заполнить их данными и только потом выполнить сценарий. Юнит-тесты, которые Иван запустил следом, уложились в 97 миллисекунд. Подготовки в таком тесте намного больше, чем в юнит-тесте: сам вызов проверяемого кода занимает одну строку, а всё остальное уходит на данные и проверки.
Иван всё равно советует писать такие тесты. Приложения обычно сложнее простых CRUD-сервисов, которые только создают, читают, обновляют и удаляют записи. К тому же приложение развивается: например, в базе появляется новая колонка, и из-за неё ломается то, что раньше работало. Разработчик думает, что всё в порядке, а интеграционный тест показывает, что связка с базой больше не работает.
Интеграционных тестов обычно гораздо меньше, чем юнит-тестов, потому что писать их сложнее. Допустим, в коде есть ветка, которая обрабатывает ошибку. Чтобы интеграционный тест в неё попал, настоящая база должна в нужный момент не отдать данные. Такие исключительные сценарии Иван проверяет юнит-тестами: мок вернёт что угодно, и тест пишется быстро. Интеграционным тестам остаются важные бизнес-сценарии с корнер-кейсами, то есть пограничными случаями, в которых вы точно хотите быть уверены. Инструменты для таких тестов, как правило, есть для любого языка, Go здесь только пример.

E2E-тесты проходят путь пользователя

E2E-тесты проверяют приложение от начала до конца, отсюда и название: end-to-end. Это самые тяжёлые и самые долгие тесты.
В криптоплатёжке Ивана E2E-тесты собраны в коллекцию Postman. Это инструмент для запросов к API, а коллекция задаёт порядок запросов к бэкенду. Тест аутентификации входит в систему под администратором, запрашивает профиль пользователя, проверяет, что работает refresh-токен, выходит из аккаунта и убеждается, что старым токеном больше нельзя пользоваться. Другой тест проверяет управление компаниями: вход, создание компании, получение её данных, список компаний, обновление.
Слово «бизнес» Иван просит не путать с деньгами. Для него это поведение приложения.

Под бизнес-сценарием я подразумеваю порядок действий, который пользователь выполняет в вашем приложении, чтобы добиться результата, за которым он пришёл.

Запустить коллекцию на стриме не вышло. E2E-тестам криптоплатёжки нужны специальные конфиги и мок внешнего сервиса, а Docker в тот вечер не завёлся. Поэтому E2E-тест в работе Иван показал на другом проекте.

Как тест ловит поломку

Сервис 3D-печати Иван начал писать в понедельник вечером, за три дня до стрима, поэтому E2E-тест там пока один. Этот тест описывает сценарий, в котором клиент заказывает печать по STL-файлу с 3D-моделью и принимает цену, которую назначил мастер. Тест проверяет и фронтенд, и бэкенд, но смотрит опять же на поведение. Он открывает главную страницу, находит кнопку «Напечатать свой файл», загружает модель, нажимает «Продолжить», переходит на страницу настройки, добавляет заказ в корзину, проверяет корзину и отправляет заказ на оценку. Потом админ открывает заказ по ID и ставит итоговую цену. Так тест проходит весь путь заказа.
Второй тест на стриме написал Claude: админ добавляет материал с цветом, и клиент может его заказать. Чтобы показать, что делает тест, Иван прошёл его шаги руками. В админке он создал пластик TPU с ценой за грамм и параметрами для слайсера, нашёл его в списке и добавил зелёный цвет. Потом от лица клиента загрузил модель и убедился, что зелёный TPU доступен для заказа.
На шаге с цветом нашёлся баг. Поле для нового цвета человек на экране не видит, а тест его находит и заполняет, потому что работает с кодом страницы. Иван назвал это одним из приколов автотестов. Пройденный тест в таком месте не значит, что живой пользователь пройдёт тот же путь.
Теперь второй тест фиксирует фичу с материалами, и Иван показал, ради чего это нужно. Он «нечаянно» переименовал путь страницы настройки заказа и запустил тесты. Тест упал, и причина была видна сразу: сценарий пытался перейти по старому адресу configure, а такого пути больше нет. Стоит вернуть старое имя, и тест снова проходит.
Тестов много не бывает, считает Иван: их стоит писать и следить за ними. Эту работу тоже можно отдать агенту. В другом проекте у Ивана есть отдельный агент, который пишет тесты вместо него, а в описании агента сказано, как их писать и что с ними делать. E2E-тестов там заметно больше, чем в сервисе 3D-печати, и сценариев они покрывают больше.

Что делать, когда тест упал

Под конец Иван дал шпаргалку на случай, когда тест покраснел. Первым делом хочется написать агенту «чини» и не разбираться. Иван советует сначала сесть и понять, что сломалось и почему.

Если раньше у вас всё работало, а агент это сломал, значит, он, скорее всего, сломал то, что ломать не должен был.

Бывает и по-другому: вы сознательно меняете бизнес-логику, и часть тестов гарантированно упадёт. Тогда агент сам скажет, что поведение изменилось и тесты нужно поправить под новую логику. Поэтому, прежде чем просить агента починить тест, разберитесь, какой из двух случаев перед вами.

Что из этого взять в работу

Распределите проверки по уровням. Отдельные функции и редкие ветки с ошибками покрывайте юнит-тестами на моках: они быстрые, и их может быть много. Важные сценарии с базой и внешними сервисами проверяйте интеграционными тестами с настоящими зависимостями. Пути, ради которых пользователь приходит в приложение, закрывайте E2E-тестами.
Запишите в README, как запускать каждый вид тестов. Тяжёлым тестам нужны Docker и особые конфиги, и даже Иван на стриме выяснял через Claude, как завести E2E-тесты своей криптоплатёжки.
Сформулируйте главные бизнес-сценарии как последовательность действий пользователя и результат, за которым он пришёл. Это готовое задание для E2E-теста, и его можно отдать агенту. Новый тест один раз пройдите руками, ведь он работает с кодом страницы и может не заметить того, что мешает человеку.
Проверьте, что тест ловит поломку. Сломайте что-нибудь намеренно, например переименуйте путь страницы, и убедитесь, что тест упал с понятной причиной.
Когда тест падает после работы агента, не пишите сразу «чини». Сначала решите, сломал ли агент то, что работало, или поведение поменялось намеренно и тест пора обновить.
Заведите отдельного агента для тестов и опишите в его инструкции, как их писать и что с ними делать. Так работает сам Иван.
Трек: Вайбкодинг
Соберёте с Codex или Claude Code сайт-портфолио, сайт с онлайн-записью для заказчика и своё приложение. Проверите, как они работают, и опубликуете по ссылке.
Подробнее
Поделиться
Интересно погрузиться в крипту?
Поможем выбрать направление!