- BrainTools - https://www.braintools.ru -

От промпта к мутациям: как я перестал писать тесты руками и собрал команду из 7 AI-агентов

Кому будет полезно

  • Фронтенд-разработчикам, которые хотят мигрировать тесты с Enzyme на React Testing Library

  • Тем, кто экспериментирует с LLM для генерации кода

  • Тем, кому интересен практический опыт [1] построения мультиагентных систем

⚠️ Дисклеймер. Статья полностью про фронтенд: React, Jest/Vitest, React Testing Library. И ещё: это просто моя честная история, как было. Можно было сделать быстрее, можно было думать по-другому, но я рассказываю как есть, со всеми ошибками и тупиками.

Контекст

В первой статье should render — и что? Как мы перестали тестировать разметку и начали тестировать поведение [2] я рассказал про философию тестирования: почему снэпшоты и Enzyme ведут в тупик, зачем переходить на React Testing Library и как я провёл митап для команды. Там был чек-лист, были примеры плохих и хороших тестов, было ясное понимание как хотим и как не хотим.

Я никогда тебя не любил

Я никогда тебя не любил

Понимание, как писать правильно, это одно. Реальность совсем другое. Передо мной стояли сотни компонентов, и на каждый нужно было написать тесты с нуля. Тесты типа should render в расчёт не беру. Руками это месяцы работы, которые никто не выделит в спринт. И я начал думать: как это можно автоматизировать?

Эта статья про путь от наивного “закинул компонент в ChatGPT” до рабочего мультиагентного пайплайна, который генерирует тесты, ревьюит их по философии RTL и верифицирует через мутации.

Этап 0: наивный подход, “просто спроси LLM”

Первые попытки были максимально простыми. Скопировал код компонента, вставил в ChatGPT, попросил написать тесты.

Результат оказался предсказуемо плохим. Модель не знала ни контекста проекта, ни наших правил, ни философии тестирования. Она генерировала что-то, что выглядело как тесты, но по факту:

  • Тесты не проходят

  • TypeScript ошибки [3] на каждом шагу

  • Много анти-паттернов, от которых я пытался уйти

  • Ничего не знало про наши моки, стор, роутинг

Без контекста, без правил, без философии LLM просто галлюцинировала “среднестатистические тесты из интернета”.

Мой уровень гениальности

Мой уровень гениальности [4]

Этап 1: пайплайн в N8N, первая попытка системного подхода

Я решил, что дело в промпте и контексте. Если дать модели достаточно контекста, она должна справиться.

Собрал пайплайн в N8N. Подключил ChatGPT 4.5 через API. Написал большой системный промпт: кто ты, что делаешь, вот философия тестирования, вот хорошие паттерны, вот плохие, вот код компонента, пиши.

Один огромный запрос со всем контекстом.

Не сработало. Тогда я разбил пайплайн на шаги:

  1. Анализ компонента: определить, нужно ли вообще что-то тестировать и какая тут сложность

  2. Классификация: разбить тесты по группам: простые, средние, сложные

  3. Генерация: написать тесты с подходящими примерами для каждой группы

Стало немного лучше, но результаты всё ещё были не рабочими. Тесты очень редко запускались, TypeScript выдавал кучу ошибок. Главная проблема в том, что модель работала в вакууме. Она видела код одного компонента, но не видела проект. Не могла проверить импорты, не знала про реальные типы, не могла запустить то, что написала.

На этом этапе я забросил идею. Мне казалось, что LLM пока просто не готовы для такой задачи. Хотя в голове даже представил, какой из этого можно сделать продукт, который так и остался на уровне идеи и не дошёл до реализации.

Стартап в голове: три ступени

Уровень 1: веб-интерфейс. Закидываешь код компонента, получаешь тесты. Просто и красиво. Но постфактум понимаю, что обречено на провал, потому что слишком мало контекста. Модель не может ничего проверить, не видит проект.

Уровень 2: расширение для VS Code. Прямо в IDE нажимаешь кнопку рядом с компонентом. Расширение берёт код, подтягивает философию тестирования, создаёт файл с тестами. Как встроенный прогон тестов через Jest/Vitest, только для генерации. Мне казалось, это прям красиво. Но я не понимал, как это реализовать технически.

Уровень 3: инструмент для полной миграции. Расширение сканирует всю кодовую базу, собирает список компонентов и делает полную миграцию одним кликом. Задача на часы, но запускается одной командой.

Красивая картинка, недостижимая на тот момент для меня.

Ну все же так делают в мыслях, нет?

Ну все же так делают в мыслях, нет?

Этап 2: Cursor, ближе, но не то

Через какое-то время я начал пользоваться Cursor. С большим отставанием от общей волны хайпа и выхода IDE. Это уже новый уровень: доступ к проекту, он видел файлы, мог подтягивать контекст.

Я просил: “напиши тесты для этого компонента”. Cursor писал. Результаты были лучше, чем у голого ChatGPT, потому что он хотя бы видел импорты и типы. Но без хороших промптов и чётких правил тесты всё равно получались “ниже среднего”. Философия RTL, пользовательский подход, правильные моки: всё это нужно было объяснять каждый раз.

Догадался соединить одно с другим. Наработки по промптам из N8N и возможности Cursor, прописал на уровне правил проекта: философию, примеры, приблизительную модель принятия решений.

Казалось бы, должен быть качественный скачок, но на тот момент и этого тоже не случилось. Всё равно тесты были 50/50, с большим количеством правок и ручной работы. Не хватало ни мощности модели, ни структурности, ни правил принятия решений, и за длинное время работы в одном контекстном окне модель начинала галлюцинировать.

Этап 3: Claude Code, и тут картинка наконец складывается

Всё изменилось, когда появился Claude Code с агентами и навыками.

А в чём разница то?

А в чём разница то?

Навыки это чистые наборы инструкций на какую либо тему без рабочих рук которые её выполняют. А вот агенты это уже работяги с выбранной моделью, инструкцией, отдельным окном контекста. И главное весь контект проекта тоже есть Claude Code работает изнутри: видит все файлы, может запускать команды, проверять результат.

Мой “уровень 2” из мечты вдруг оказался реализуемым. Причём сразу с прицелом на третий.

Первый рабочий прототип: 3 агента

Я начал строить мультиагентный пайплайн. Сначала думал, что все участники будут агентами, но оказалось, что агенты могут спавнить только субагентов, а не вызывать других агентов на своём уровне. Поэтому главным оркестратором стал навык unit-tester, который знает полный пайплайн и вызывает нужных агентов в нужном порядке.

Первый прототип:

  1. test-planner читает код компонента, анализирует его тип (простой лист-компонент или контейнер с логикой [5]), планирует тест-кейсы

  2. test-writer получает план, пишет готовый .test.tsx файл

  3. test-validator проверяет, что всё компилируется: ESLint, TypeScript, запуск тестов

Плюс конфигурационный файл .test-pipeline.yaml для каждого проекта: где лежат моки, какой стор, Jest или Vitest, какие специфические правила. Это позволяет шарить систему между разными проектами.

Уже и кофе кончился...

Уже и кофе кончился…

И вот здесь случился качественный скачок. По сравнению с N8N-пайплайном и Cursor это был совершенно другой уровень:

  • Агенты работали с реальным проектом, а не с изолированным куском кода

  • Они могли проверить то, что написали: запустить тесты, увидеть ошибки

  • Каждый агент фокусировался на своей задаче, а не пытался сделать всё сразу

  • Конфиг давал контекст проекта: где моки, как устроен стор, какие конвенции

Этап 4: когда зелёные тесты врут

Валидатор проверял, что тесты запускаются. Но “запускается” ещё не значит “хорошо написан”. Тест может пройти и при этом цепляться за детали реализации, использовать антипаттерны или тестировать совсем не то, что важно пользователю.

Так появился четвёртый агент, test-reviewer. Он проверяет написанные тесты по философии RTL:

  • Тестируем поведение [6], а не реализацию?

  • Обращаемся к элементам через роли и лейблы, а не через CSS-классы?

  • Описания тест-кейсов понятные и информативные?

  • Используем константы, а не захардкоженные строки?

Ревьюер выставляет оценку от 1 до 10. Если меньше 9, тесты уходят на второй круг: планировщик пересматривает план, писатель переписывает. Максимум 3 попытки.

Этап 5: мутационное тестирование, и вот тут всё по-настоящему изменилось

Это идея, которая перевернула для меня восприятие [7] качества тестов.

А что если нет?

А что если нет?

Даже если тест проходит ревью по философии RTL и компилируется, как убедиться, что он реально ловит баги? Что это не ложноположительный тест, который просто “проходит” и ни о чём не говорит?

Ответ: мутационное тестирование.

Идея простая. Я ломаю компонент определённым образом и проверяю, что тест это заметил. Если тест прошёл при сломанном компоненте, он бесполезен.

Два новых агента:

mutation-planner

Анализирует написанные тест-кейсы и планирует мутации. Для каждого тест-кейса определяет: что конкретно нужно сломать в компоненте, чтобы этот тест упал.

Например:

  • Тест проверяет, что кнопка “Отправить” заблокирована при пустом email, значит мутация: убрать проверку disabled

  • Тест проверяет отображение ошибки, значит мутация: убрать рендер сообщения об ошибке

  • Тест проверяет вызов API, значит мутация: закомментировать вызов

test-verifier

Выполняет мутации строго последовательно (это критически важно). Для каждой мутации:

  1. Запускает тест-кейс, убеждается, что он проходит на неизменённом компоненте ✅

  2. Делает бэкап компонента

  3. Применяет мутацию, ломает компонент по плану

  4. Запускает тест-кейс снова, и он должен упасть ❌

  5. Восстанавливает компонент из бэкапа

  6. Запускает тест-кейс ещё раз, и он должен снова пройти ✅

Если все три шага сходятся, мутация пройдена, тест реально работает. Если тест не упал при сломанном компоненте, это слабый тест-кейс и тесты уходят на доработку.

Целевой показатель: 100% catch rate. Если меньше, писатель дописывает тесты, и мутации прогоняются заново. Максимум 3 попытки.

Мы будем тестировать тебя полностью

Мы будем тестировать тебя полностью

Итоговая архитектура: React Test Agents

Вот к чему я в итоге пришёл. Семь агентов, шесть шагов:

От промпта к мутациям: как я перестал писать тесты руками и собрал команду из 7 AI-агентов - 8

Распределение моделей

Не все задачи требуют одинаковой “силы” модели:

  • Opus (мощнее, дороже): планирование, написание и ревью тестов, планирование мутаций. Здесь важно глубокое понимание кода и философии

  • Sonnet (быстрее, дешевле): валидация (запуск ESLint/TS/тестов) и выполнение мутаций (механическая работа по чёткой инструкции)

Вы можете использовать любого провайдера и любые модели. Важно понимать три вещи: насколько модель умная, насколько хорошо она кодит и сколько стоит.

Седьмой агент: commit-analyzer

Отдельно стоит commit-analyzer. Он встраивает генерацию тестов в обычный рабочий процесс.

commit-analyzer делает одну простую вещь: анализирует коммиты от указанного хэша до HEAD и выдаёт список затронутых компонентов с типом изменений. Мелкие правки, серьёзные изменения или совсем новый компонент.

Дальше в дело вступает основной навык unit-tester, и вот тут начинается самое интересное. Он работает в интерактивном режиме: приходит ко мне в терминал и говорит “для компонента X надо дописать тесты, вот что изменилось, пишем?”. Я отвечаю approve или skip. Потом следующий компонент. И так далее.

За одну команду можно пройтись по всем затронутым изменениями файлам. Каждый компонент обрабатывается последовательно. Контекстное окно не забивается, потому что каждый агент работает в своём изолированном контексте. Полный прогон для компонента средней сложности занимает порядка 10 минут работы Claude Code.

Я уже использую это в повседневной работе. Пофиксил баг, написал фичу, запустил тестировщик, прошёлся по изменённым компонентам. Approve, approve, skip, approve. Готово.

А я не так уж и безнадёжен

А я не так уж и безнадёжен

Что я понял по пути

1. Контекст решает всё

Один и тот же LLM генерирует мусор без контекста и рабочие тесты с контекстом. Разница между “закинул код в чат” и “агент видит весь проект” как между гуглением рецепта и работой с шеф-поваром на твоей кухне.

2. Разделяй и властвуй

Один агент, который делает всё, работает хуже, чем цепочка специализированных агентов. Каждый фокусируется на своей задаче: один планирует, другой пишет, третий проверяет. Как в настоящей команде разработки.

Но есть ещё одна причина, почему разделение критично. Каждый агент работает в своём контекстном окне. Основной оркестратор не забивается лишней информацией. Агенты меньше галлюцинируют, чётче понимают задачу, работают лаконичнее. Если запихнуть всё в один контекст, качество деградирует с каждым шагом.

3. Мутации как единственная честная проверка

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

4. Итерации важнее первого результата

Пайплайн рассчитан на то, что с первого раза будет не идеально. Ревью с порогом 9/10 и мутации с 100% catch rate, это встроенные петли обратной связи.

Текущий статус

Пайплайн отлаживался на наборе эталонных компонентов: простой (кнопка), средний (форма с валидацией) и сложный (с бизнес-логикой и стором). Порядка 10 итераций на простой, столько же на средний, и так до тех пор, пока пайплайн не начал выдавать стабильный результат.

Полная миграция не завершена, Enzyme до сих пор живёт в проекте. Мы не выделяли отдельное время на “переписать всё”. Вместо этого миграция идёт в процессе работы: тронул компонент, переписал заодно тесты. 40+ компонентов уже прошли через пайплайн на проде.

Знаете, что мне нравится больше всего? Жить в это время. Не именно пока агенты пишет тесты, а вообще сейчас, когда технологии развиваются так стремительно. Играться с такими штуками. Когда я только начинал учиться веб-разработке, я и представить не мог, что буду строить мультиагентные системы для генерации тестов. Что AI будет писать код, ревьюить его и намеренно ломать, чтобы проверить качество. Мир сильно изменился, и мне это нравится.

Когда миграция будет полностью завершена, я напишу третью статью. Посчитаем математику [8]: сколько заняло времени, какой итоговый процент покрытия, сколько стоило в токенах. Цифры, результаты. До скорого!

Автор: M0SEY

Источник [9]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/28365

URLs in this post:

[1] опыт: http://www.braintools.ru/article/6952

[2] should render — и что? Как мы перестали тестировать разметку и начали тестировать поведение: https://habr.com/ru/articles/1015772

[3] ошибки: http://www.braintools.ru/article/4192

[4] гениальности: http://www.braintools.ru/article/4566

[5] логикой: http://www.braintools.ru/article/7640

[6] поведение: http://www.braintools.ru/article/9372

[7] восприятие: http://www.braintools.ru/article/7534

[8] математику: http://www.braintools.ru/article/7620

[9] Источник: https://habr.com/ru/articles/1020066/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1020066

www.BrainTools.ru

Rambler's Top100