От промпта к мутациям: как я перестал писать тесты руками и собрал команду из 7 AI-агентов. ai.. ai. claude code.. ai. claude code. llm.. ai. claude code. llm. react.. ai. claude code. llm. react. react testing library.. ai. claude code. llm. react. react testing library. ReactJS.. ai. claude code. llm. react. react testing library. ReactJS. TypeScript.. ai. claude code. llm. react. react testing library. ReactJS. TypeScript. автоматизация.. ai. claude code. llm. react. react testing library. ReactJS. TypeScript. автоматизация. мутационное тестирование.. ai. claude code. llm. react. react testing library. ReactJS. TypeScript. автоматизация. мутационное тестирование. Программирование.. ai. claude code. llm. react. react testing library. ReactJS. TypeScript. автоматизация. мутационное тестирование. Программирование. Проектирование и рефакторинг.. ai. claude code. llm. react. react testing library. ReactJS. TypeScript. автоматизация. мутационное тестирование. Программирование. Проектирование и рефакторинг. тестирование.. ai. claude code. llm. react. react testing library. ReactJS. TypeScript. автоматизация. мутационное тестирование. Программирование. Проектирование и рефакторинг. тестирование. Тестирование веб-сервисов.

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

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

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

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

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

Контекст

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Этап 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 читает код компонента, анализирует его тип (простой лист-компонент или контейнер с логикой), планирует тест-кейсы

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Даже если тест проходит ревью по философии 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 будет писать код, ревьюить его и намеренно ломать, чтобы проверить качество. Мир сильно изменился, и мне это нравится.

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

Автор: M0SEY

Источник