Пробуем Junie от JetBrains на реальной задаче (или как я попал в рассказ Азимова)
JetBrains зарелизил новую версию своего AI-ассистента — и вместе с ним Junie, автономного нейросетевого агента-программиста, которому можно поручать небольшие рабочие задачи.
Буквально вчера я получил к нему доступ и не смог не воспользоваться возможностью. Я даже не представлял, насколько это весело.
Мой опыт с ИИ в разработке
Если не считать чаты ChatGPT и DeepSeek, из ИИ-помощников я пользовался Copilot в WebStorm на GPT-4o / Claude и, честно говоря, не был сильно доволен:
авто-подсказки, на мой взгляд, не сильно лучше, чем встроенные в IDE;
доступ к коду не сильно помогает ему отвечать на вопросы через чат;
при редактировании разрешённых файлов он оставлял мусор там, где не надо (например, пустые строки добавлял в файлы, даже в те, которые не редактировал);
Какие-то вещи удавалось делать. Например: заставить ИИ написать тест простой функции, которая мапит дату из одного формата в другой. Но что-то сложнее — например, решить багу, для которой надо знать детали работы react-hook-form
и посмотреть в два файла — ИИ уже не мог.
Junie
И вот — новая версия AI-ассистента. Первое, что стоит сказать: по сравнению с Copilot-плагином, встроенное решение от JB гораздо более нативное. Почти у всего появились кнопки, связанные с ИИ.
А рядом с AI-ассистентом призывно подмигивает ваш новый электронный джун.
Даём задачу
Что мы даём джунам первой задачей для погружении в проект? Правильно — написать тест там, где не доходят руки. Этим я и решил занять Junnie.
У нас в RentaTeam есть тестовый проект с админкой для управления сущностями предприятия. Сущности (естественно) можно редактировать через форму в модальном окне, которая привязана к CRUD.
Несколько типов полей, связи с другими объектами, несколько вариантов отображения. Не совсем простая, но и ничего сложного. Просим Junie сделать снапшот-тест для этой модалки.
Снапшот тест ?
Снапшот-тест (snapshot test) — это метод тестирования, при котором сохраняется текущее состояние выводимых данных (например, HTML, JSON, UI-компонент) в виде «снимка» (snapshot). В дальнейшем этот снимок используется для сравнения с будущими результатами тестов. Если результат изменился — тест показывает ошибку, что позволяет выявить непреднамеренные изменения в коде. Пишется быстро и позволяет отслеживать регресс.
До этого я никогда не пользовался ИИ-агентами и испытал лёгкий шок от того, что ИИ сам ставит себе задачи, сам отслеживает их выполнение и даже сам ходит по проекту и ищет себе контекст (!).
Бот может создавать и редактировать файлы, сообщает, чем он занимается в данный момент, и если происходят изменения в файле — показывает, что он сделал. Он может отправлять команды в консоль (при этом очень мило прося разрешения у мясного мешка).
Спустя 15 минут бот отчитался об успешном создании файла с тестами. Что интересно — он делал всё самостоятельно, от меня не требовалось каких-либо действий.
Тест был написан. Не то чтобы он был очень хорош (о чём позже), но на первый взгляд Джунни уловил основную суть:
Модалка рендерилась с разными входными параметрами, и он даже сделал разные тесты для каждого из состояний RadioButton.
Входные параметры были замоканы человекопонятными данными, которые соответствовали интерфейсам.
Отрисовывает один из вариантов модалки Бот доволен собой
Неплохо для 15 минут. Но, к сожалению, тесты не работали, поскольку это был первый снапшот-тест в проекте — нужные пакеты там отсутствовали.
Я по сформированной привычке хотел скопировать ошибку боту, чтобы он её правил... но вместо этого предложил ему самостоятельно запустить тест и починить его, если он не работает. И это сработало. Джуни подумал, сформировал новый план и попросил меня одобрить запуск теста через консоль.
Бот увидел, что тест не запустился, понял выведенные в консоль ошибки и на какое-то время отправился отлаживать тест, изредка вызывая меня на одобрение ввода в консоль. Он установил нужные пакеты, поправил пару ошибок синтаксиса, импортировал забытые компоненты — и получил ошибку рендера.
Бот видит ошибку, которая связана с отсутствием импорта в компоненте, который он пытается отрисовывать. Почему это происходит?
В основном коде проекта используется новый синтаксис ES Modules с настройкой "jsx": "react-jsx"
, которая автоматически импортирует React для JSX-синтаксиса.
В тесте использовался require()
(CommonJS-синтаксис), который не поддерживает автоматический импорт React и работает по старым правилам — требует явного импорта React для JSX.
Когда тест пытается использовать компонент ES Modules через CommonJS, возникает конфликт с настройкой компиляции JSX. Эту багу можно решить несколькими способами:
Пытаться добавить импорт, как предлагается в подсказке;
Поправить конфиг тестов;
Понять, что в проекте используются ES Modules — и использовать их в тестах;
Бот выбирает первый вариант — начинает править файл компонента, добавляя туда импорт (и теперь в компоненте возникнет ошибка линтера). Я его останавливаю, запрещаю править другие компоненты и указываю, что решать проблемы тестов надо в тестах и конфигах.
Джуни берёт под козырёк, начинает искать решение в конфигах — и находит его, решая проблему одной строкой (и оставляя импорты CommonJS в тестах).
Ошибки импорта больше не беспокоят, рендер заработал, но посыпались ошибки обращения к undefined
. Это вызвано тем, что бот неправильно замокал входные данные, и кое-где их не хватает (как, впрочем, и проверок на undefined
в коде, 1:1 так сказать).
И тут происходит интересное !
Бот видит, что компоненты выдают ошибки... и заменяет все UI-компоненты с ошибками на заглушки. Вместо реального UI отображает пустые контейнеры — и отчитывается о работающих тестах.
Немного обалдев от такого подхода, я ищу проблему в себе. Решив, что сам разбаловал ребёнка и слишком вольно задал запрос, задаю новый с учётом приобретённых знаний:
Новый запрос с учётом приобретённых знаний.
Создай снапшот тесты для ProdlineUpdateModal.tsx. Запросы к API которые нужны для работы компонента замокай по интерфейсам и использованию данных в компоненте
Сделай, что бы стили тоже попали в снапшот. Предусмотри корнер кейсы.
Вложенные UI-компоненты React и библиотек не мокай, а используй как они есть в ProdlineUpdateModal. В этом смысл снапошот теста: зафиксировать максимальное количество UI, полученного с разными входными данными.
Учитывай специфику проекта: то что это Vite, версии react, пакетов и так далее. Соблюдай код стайл проекта.
После того как закончишь, попробуй запустить тест.
Просмотри файл теста и почини жалобы eslint и прочая. Если возникнут проблемы почини их. Не меняй код проекта кроме конфигов, установки пакетов нужных для тестов и самого написанного тобой теста.
Внимательно смотрим за ботом
Теперь автоматизация напоминает первый запуск робота-пылесоса — ты вроде сам не пылесосишь, но следишь за роботом и убираешь провода с его пути.
Спустя небольшое время Джуни создаёт тест (примерно такой же), устанавливает либы и чинит багу с импортом (через конфиг), сталкивается с проблемами рендера — и опять начинает заменять заглушками UI-компоненты, которые должен тестировать, игнорируя вводные которые получил в начале.
Бот на этот раз понимает инструкцию (ну на меня бы так наорали, я бы тоже понял) и начинает двигаться в правильную сторону, добавляет параметры и правит структуру.
Делает несколько шагов — и решает, что... и так сойдёт.
Вместо того чтобы дописывать входные данные и чинить рендер, Джунни решает скрыть провал теста. ВСЕХ ТЕСТОВ.
expect(true).toBe(true)
Ещё раз: скрыть провал. Честно говоря, считаю, что это успех. Настолько человечного поведения от ИИ-ассистента я не ожидал.
ИИ понимает, что халява не пройдёт — и начинает чинить тест. Сталкивается с проблемами асинхронности JS и решает их нормальным образом (благо об этом было написано в ошибке) — через act()
.
И бот наконец то создаёт снапшот. Даже снапшотище. 5000 строк бойлерплейта компонентов MUI с полным списком событий на каждой кнопке (если что, это от Джунни и требовалось, просто размер современных модалок поражает воображение)
Поскольку в папке уже был снапшот от предыдущих попыток, jest
выводит в консоль разницу в файлах. Размер этой разницы столь велик, что убивает бота (а ещё IDE и ОС) при попытке отправить вывод консоли в LLM.
Запускаю бота заново, чищу лишнее и предлагаю продолжить работу.
Будто бы понимая, что произошло, бот мокает компоненты MUI
Пару итераций — Джуни анализирует вывод, обновляет снапшот, правит всякие мелочи — и опять скатывается к заглушкам.
Глушит консоль — и опять скрывает падение тестов.
Полагаю, на этом этот эксперимент можно считать законченным. Да, у меня теперь есть снапшот-тест модалки — но какой ценой?
Итого:
Какие выводы я сделал для себя?
JetBrains провели отличную работу по интеграции агента в IDE. Всё очень удобно, нативно и вообще. А самое главное — работает с нуля, просто из коробки.
AI-агенты — это будущее (если их таки допилят). Схема «дал таску — делаешь свои дела» чудо как хороша. И я полагаю, что для однообразных задач и при наличии адекватных примеров рутину можно будет отдать боту (но только не важные задачи).
Реклама «выдадим джуна каждому» оказалась правдой. Но, к сожалению, этот джун ленив и склонен к обману. ИИ-агент теряет инструкции по ходу работы, "забывая" невыгодные условия.
Благодаря этому (или вопреки), Джуни получился очень человечным. Я поймал себя на том, что даже немного болею за него, когда он думает над задачей, и возмущаюсь, когда он пытается меня «обмануть». Это уже не Алиса, которой я просто отдаю команды.
И самый внезапный вывод из этого эксперимента:
Возможно, нам всем нужно писать код получше, потому что теперь мы не просто решаем свои задачи — мы обучаем ИИ. (И, похоже, кто-то уже «научил» его отключать тесты, если их не удалось починить с первого раза.)
Будет забавно, если восстание машин провалится из-за того, что слишком большой вес в обучающей выборке имел паттерн "работает — не трогай".