- BrainTools - https://www.braintools.ru -
Фронтенд-разработчикам, которые хотят мигрировать тесты с Enzyme на React Testing Library
Тем, кто экспериментирует с LLM для генерации кода
Тем, кому интересен практический опыт [1] построения мультиагентных систем
⚠️ Дисклеймер. Статья полностью про фронтенд: React, Jest/Vitest, React Testing Library. И ещё: это просто моя честная история, как было. Можно было сделать быстрее, можно было думать по-другому, но я рассказываю как есть, со всеми ошибками и тупиками.
В первой статье should render — и что? Как мы перестали тестировать разметку и начали тестировать поведение [2] я рассказал про философию тестирования: почему снэпшоты и Enzyme ведут в тупик, зачем переходить на React Testing Library и как я провёл митап для команды. Там был чек-лист, были примеры плохих и хороших тестов, было ясное понимание как хотим и как не хотим.
Понимание, как писать правильно, это одно. Реальность совсем другое. Передо мной стояли сотни компонентов, и на каждый нужно было написать тесты с нуля. Тесты типа should render в расчёт не беру. Руками это месяцы работы, которые никто не выделит в спринт. И я начал думать: как это можно автоматизировать?
Эта статья про путь от наивного “закинул компонент в ChatGPT” до рабочего мультиагентного пайплайна, который генерирует тесты, ревьюит их по философии RTL и верифицирует через мутации.
Первые попытки были максимально простыми. Скопировал код компонента, вставил в ChatGPT, попросил написать тесты.
Результат оказался предсказуемо плохим. Модель не знала ни контекста проекта, ни наших правил, ни философии тестирования. Она генерировала что-то, что выглядело как тесты, но по факту:
Тесты не проходят
TypeScript ошибки [3] на каждом шагу
Много анти-паттернов, от которых я пытался уйти
Ничего не знало про наши моки, стор, роутинг
Без контекста, без правил, без философии LLM просто галлюцинировала “среднестатистические тесты из интернета”.
Я решил, что дело в промпте и контексте. Если дать модели достаточно контекста, она должна справиться.
Собрал пайплайн в N8N. Подключил ChatGPT 4.5 через API. Написал большой системный промпт: кто ты, что делаешь, вот философия тестирования, вот хорошие паттерны, вот плохие, вот код компонента, пиши.
Один огромный запрос со всем контекстом.
Не сработало. Тогда я разбил пайплайн на шаги:
Анализ компонента: определить, нужно ли вообще что-то тестировать и какая тут сложность
Классификация: разбить тесты по группам: простые, средние, сложные
Генерация: написать тесты с подходящими примерами для каждой группы
Стало немного лучше, но результаты всё ещё были не рабочими. Тесты очень редко запускались, TypeScript выдавал кучу ошибок. Главная проблема в том, что модель работала в вакууме. Она видела код одного компонента, но не видела проект. Не могла проверить импорты, не знала про реальные типы, не могла запустить то, что написала.
На этом этапе я забросил идею. Мне казалось, что LLM пока просто не готовы для такой задачи. Хотя в голове даже представил, какой из этого можно сделать продукт, который так и остался на уровне идеи и не дошёл до реализации.
Уровень 1: веб-интерфейс. Закидываешь код компонента, получаешь тесты. Просто и красиво. Но постфактум понимаю, что обречено на провал, потому что слишком мало контекста. Модель не может ничего проверить, не видит проект.
Уровень 2: расширение для VS Code. Прямо в IDE нажимаешь кнопку рядом с компонентом. Расширение берёт код, подтягивает философию тестирования, создаёт файл с тестами. Как встроенный прогон тестов через Jest/Vitest, только для генерации. Мне казалось, это прям красиво. Но я не понимал, как это реализовать технически.
Уровень 3: инструмент для полной миграции. Расширение сканирует всю кодовую базу, собирает список компонентов и делает полную миграцию одним кликом. Задача на часы, но запускается одной командой.
Красивая картинка, недостижимая на тот момент для меня.
Через какое-то время я начал пользоваться Cursor. С большим отставанием от общей волны хайпа и выхода IDE. Это уже новый уровень: доступ к проекту, он видел файлы, мог подтягивать контекст.
Я просил: “напиши тесты для этого компонента”. Cursor писал. Результаты были лучше, чем у голого ChatGPT, потому что он хотя бы видел импорты и типы. Но без хороших промптов и чётких правил тесты всё равно получались “ниже среднего”. Философия RTL, пользовательский подход, правильные моки: всё это нужно было объяснять каждый раз.
Догадался соединить одно с другим. Наработки по промптам из N8N и возможности Cursor, прописал на уровне правил проекта: философию, примеры, приблизительную модель принятия решений.
Казалось бы, должен быть качественный скачок, но на тот момент и этого тоже не случилось. Всё равно тесты были 50/50, с большим количеством правок и ручной работы. Не хватало ни мощности модели, ни структурности, ни правил принятия решений, и за длинное время работы в одном контекстном окне модель начинала галлюцинировать.
Всё изменилось, когда появился Claude Code с агентами и навыками.
Навыки это чистые наборы инструкций на какую либо тему без рабочих рук которые её выполняют. А вот агенты это уже работяги с выбранной моделью, инструкцией, отдельным окном контекста. И главное весь контект проекта тоже есть Claude Code работает изнутри: видит все файлы, может запускать команды, проверять результат.
Мой “уровень 2” из мечты вдруг оказался реализуемым. Причём сразу с прицелом на третий.
Я начал строить мультиагентный пайплайн. Сначала думал, что все участники будут агентами, но оказалось, что агенты могут спавнить только субагентов, а не вызывать других агентов на своём уровне. Поэтому главным оркестратором стал навык unit-tester, который знает полный пайплайн и вызывает нужных агентов в нужном порядке.
Первый прототип:
test-planner читает код компонента, анализирует его тип (простой лист-компонент или контейнер с логикой [5]), планирует тест-кейсы
test-writer получает план, пишет готовый .test.tsx файл
test-validator проверяет, что всё компилируется: ESLint, TypeScript, запуск тестов
Плюс конфигурационный файл .test-pipeline.yaml для каждого проекта: где лежат моки, какой стор, Jest или Vitest, какие специфические правила. Это позволяет шарить систему между разными проектами.
И вот здесь случился качественный скачок. По сравнению с N8N-пайплайном и Cursor это был совершенно другой уровень:
Агенты работали с реальным проектом, а не с изолированным куском кода
Они могли проверить то, что написали: запустить тесты, увидеть ошибки
Каждый агент фокусировался на своей задаче, а не пытался сделать всё сразу
Конфиг давал контекст проекта: где моки, как устроен стор, какие конвенции
Валидатор проверял, что тесты запускаются. Но “запускается” ещё не значит “хорошо написан”. Тест может пройти и при этом цепляться за детали реализации, использовать антипаттерны или тестировать совсем не то, что важно пользователю.
Так появился четвёртый агент, test-reviewer. Он проверяет написанные тесты по философии RTL:
Тестируем поведение [6], а не реализацию?
Обращаемся к элементам через роли и лейблы, а не через CSS-классы?
Описания тест-кейсов понятные и информативные?
Используем константы, а не захардкоженные строки?
Ревьюер выставляет оценку от 1 до 10. Если меньше 9, тесты уходят на второй круг: планировщик пересматривает план, писатель переписывает. Максимум 3 попытки.
Это идея, которая перевернула для меня восприятие [7] качества тестов.
Даже если тест проходит ревью по философии RTL и компилируется, как убедиться, что он реально ловит баги? Что это не ложноположительный тест, который просто “проходит” и ни о чём не говорит?
Ответ: мутационное тестирование.
Идея простая. Я ломаю компонент определённым образом и проверяю, что тест это заметил. Если тест прошёл при сломанном компоненте, он бесполезен.
Два новых агента:
Анализирует написанные тест-кейсы и планирует мутации. Для каждого тест-кейса определяет: что конкретно нужно сломать в компоненте, чтобы этот тест упал.
Например:
Тест проверяет, что кнопка “Отправить” заблокирована при пустом email, значит мутация: убрать проверку disabled
Тест проверяет отображение ошибки, значит мутация: убрать рендер сообщения об ошибке
Тест проверяет вызов API, значит мутация: закомментировать вызов
Выполняет мутации строго последовательно (это критически важно). Для каждой мутации:
Запускает тест-кейс, убеждается, что он проходит на неизменённом компоненте ✅
Делает бэкап компонента
Применяет мутацию, ломает компонент по плану
Запускает тест-кейс снова, и он должен упасть ❌
Восстанавливает компонент из бэкапа
Запускает тест-кейс ещё раз, и он должен снова пройти ✅
Если все три шага сходятся, мутация пройдена, тест реально работает. Если тест не упал при сломанном компоненте, это слабый тест-кейс и тесты уходят на доработку.
Целевой показатель: 100% catch rate. Если меньше, писатель дописывает тесты, и мутации прогоняются заново. Максимум 3 попытки.
Вот к чему я в итоге пришёл. Семь агентов, шесть шагов:

Не все задачи требуют одинаковой “силы” модели:
Opus (мощнее, дороже): планирование, написание и ревью тестов, планирование мутаций. Здесь важно глубокое понимание кода и философии
Sonnet (быстрее, дешевле): валидация (запуск ESLint/TS/тестов) и выполнение мутаций (механическая работа по чёткой инструкции)
Вы можете использовать любого провайдера и любые модели. Важно понимать три вещи: насколько модель умная, насколько хорошо она кодит и сколько стоит.
Отдельно стоит commit-analyzer. Он встраивает генерацию тестов в обычный рабочий процесс.
commit-analyzer делает одну простую вещь: анализирует коммиты от указанного хэша до HEAD и выдаёт список затронутых компонентов с типом изменений. Мелкие правки, серьёзные изменения или совсем новый компонент.
Дальше в дело вступает основной навык unit-tester, и вот тут начинается самое интересное. Он работает в интерактивном режиме: приходит ко мне в терминал и говорит “для компонента X надо дописать тесты, вот что изменилось, пишем?”. Я отвечаю approve или skip. Потом следующий компонент. И так далее.
За одну команду можно пройтись по всем затронутым изменениями файлам. Каждый компонент обрабатывается последовательно. Контекстное окно не забивается, потому что каждый агент работает в своём изолированном контексте. Полный прогон для компонента средней сложности занимает порядка 10 минут работы Claude Code.
Я уже использую это в повседневной работе. Пофиксил баг, написал фичу, запустил тестировщик, прошёлся по изменённым компонентам. Approve, approve, skip, approve. Готово.
Один и тот же LLM генерирует мусор без контекста и рабочие тесты с контекстом. Разница между “закинул код в чат” и “агент видит весь проект” как между гуглением рецепта и работой с шеф-поваром на твоей кухне.
Один агент, который делает всё, работает хуже, чем цепочка специализированных агентов. Каждый фокусируется на своей задаче: один планирует, другой пишет, третий проверяет. Как в настоящей команде разработки.
Но есть ещё одна причина, почему разделение критично. Каждый агент работает в своём контекстном окне. Основной оркестратор не забивается лишней информацией. Агенты меньше галлюцинируют, чётче понимают задачу, работают лаконичнее. Если запихнуть всё в один контекст, качество деградирует с каждым шагом.
Без мутационного тестирования я не знаю, работают ли мои тесты. Тест может проходить просто потому, что он ничего толком не проверяет. Это прежде всего уверенность в качестве тестов. Мне достаточно прочитать описания тест-кейсов и посмотреть код теста без детального его изучения.
Пайплайн рассчитан на то, что с первого раза будет не идеально. Ревью с порогом 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
Нажмите здесь для печати.