Две недели на первый PR — и это нормально
Когда я впервые попросил LLM (тогда это был GPT-4.5 Turbo, июнь 2025) написать тесты для модуля платежей, я ожидал магии. Через 15 минут я получил 200 строк прекрасного кода на Python с использованием pytest. Выглядело идеально. До первого запуска.
Тесты падали с ошибками, которые невозможно было воспроизвести. Моки не соответствовали реальным ответам API. А assertions проверяли то, что никому не нужно. Знакомо? Тот PR мы закрыли только через 14 дней. И дело не в ИИ — дело в подходе.
Эта статья — не очередной теоретический обзор. Это хроника внедрения ИИ-генерации тестов в продуктовую команду из 12 разработчиков и 4 QA. С кровью, потом и галлюцинациями. Я расскажу, что сработало, что нет, и как превратить LLM из дорогой игрушки в рабочую лошадку.
Ключевая мысль: ИИ не пишет хорошие тесты сам по себе. Он пишет тесты, которые соответствуют вашему промпту и контексту. Хороший тест — это результат хорошего промпта + качественного контекста + человеческой валидации.
Проблема: почему 95% пилотов превращаются в дорогой хлам
Прежде чем показывать решение, давайте разберемся, почему большинство попыток внедрить ИИ в тестирование проваливаются. Я выделил три главных причины:
- Иллюзия zero-shot. Скормить LLM файл с кодом и сказать "напиши тесты" — это как попросить незнакомца написать завещание. Модель не знает ваших бизнес-правил, конвенций именования, структуры проекта. Результат — бесполезный код.
- Галлюцинации API. LLM обожает выдумывать методы и параметры. Если она "знает" библиотеку, но не видела ваш конкретный код, она сгенерирует вызовы, которые никогда не существовали. Подробнее об этом — в нашем аудите галлюцинаций LLM 2026.
- Отсутствие итераций. Первый результат никогда не бывает финальным. Но многие останавливаются на первой попытке, разочаровываются и пишут статью "ИИ не работает".
Если вы узнали себя — вы не одиноки. Я тоже прошел через это. Как мы выкарабкались — читайте ниже.
Решение: трехслойная методика "Контекст-Промпт-Валидация"
После двух недель мучений мы выработали систему, которая сократила время генерации приемлемого теста до 30-40 минут (против 4-5 часов вручную). Система состоит из трех слоев.
1 Контекст: чем больше, тем лучше (но не спам)
LLM нужно дать не просто сигнатуру функции, а полную картину. В наш промпт мы включаем:
- Исходный код тестируемого модуля (целиком, если возможно).
- Схему базы данных или ER-диаграмму (текстовое описание).
- Примеры успешных тестов из соседних модулей (1-2 файла).
- Описание бизнес-логики: какие кейсы критичны, какие граничные значения важны.
- Список зависимостей, которые нужно мокать, и ожидаемые ответы.
Звучит громоздко? Да. Но без этого модель будет гадать. Один из наших QA попробовал дать только код — получил тест, который проверял, что функция возвращает строку, хотя по спецификации должна возвращать словарь. ИИ просто "увидел" return "..." в примере и решил, что это всегда строка.
Кстати, о том, как правильно структурировать промпты, мы подробно разобрали в статье "Как заставить ИИ-ассистента для кода работать эффективнее". Там же — конкретные шаблоны.
2 Промпт: от новичка до сеньора за три уровня
Мы заметили, что качество тестов напрямую зависит от уровня детализации промпта. Я выделяю три уровня, которые описал в отдельной статье "Три уровня промптов для автотестов". Кратко:
- Уровень 1 (новичок): "Напиши тесты для функции X". Результат — базовые юнит-тесты, часто с багами.
- Уровень 2 (практик): "Напиши тесты для функции X с использованием pytest, моками для внешних вызовов, проверкой граничных значений". Уже лучше, но все еще не идеально.
- Уровень 3 (сеньор): Полный контекст, примеры, описание expected behavior, чек-лист того, что проверять. Результат — тесты, которые покрывают 90% сценариев, включая негативные.
Мы используем Level 3 как базовый. Но даже на этом уровне нужна валидация.
3 Валидация: человек в цикле — не баг, а фича
ИИ генерирует, человек проверяет. Это не про недоверие — это про безопасность. Кейс с Франкфуртом отлично иллюстрирует, чем заканчивается слепая вера в ИИ. В тестировании цена ошибки — баг в проде.
Наша процедура валидации:
- Проверить, что тест действительно выполняется (зеленый прогон).
- Проверить, что тест ловит реальный баг (например, внести преднамеренную ошибку в код — тест должен упасть).
- Проверить, что тест не избыточен (не дублирует другие тесты).
- Проверить читаемость: названия переменных, комментарии, структуру.
Только после этого тест попадает в репозиторий. Звучит как лишняя работа, но на практике это занимает 5-10 минут на тест вместо часов написания.
Пошаговый план: от идеи до первого ИИ-теста за полчаса
Допустим, вы решили попробовать. Вот точная последовательность, которую мы нашли рабочей.
1 Выберите один модуль-пилот
Не пытайтесь автоматизировать всё сразу. Возьмите изолированный микросервис или класс с понятной логикой. Идеально — модуль, который уже покрыт ручными тестами, чтобы было с чем сравнивать. Это снизит риски, о которых мы писали в статье про провал 95% пилотов.
2 Подготовьте контекст-пакет
Соберите в один текстовый файл:
- Код модуля.
- Примеры вызовов (например, curl запросы или Python-скрипты).
- Описание контракта (OpenAPI spec, proto-файлы).
- 1-2 примера ваших лучших тестов (стиль, паттерны).
Этот файл будет служить "памятью" для LLM. Если используете RAG-систему — еще лучше. Мы в итоге перешли на локальную RAG-базу, что описано в материале "Автоматизация тестирования: как RAG + LLM генерируют Java-тесты в IDE".
3 Напишите промпт уровня "сеньор"
Вот реальный шаблон, который мы используем (анонимизирован):
Ты — senior QA engineer. Напиши pytest тесты для модуля payment_processor.py.
Контекст:
- Код модуля (см. ниже).
- База данных: таблицы users, orders, payments. Связи: orders.user_id -> users.id, payments.order_id -> orders.id.
- Внешние зависимости: Stripe API (мокать с помощью responses библиотеки).
- Пример успешного теста из соседнего модуля (см. ниже).
Требования к тестам:
1. Покрыть: успешный платеж, недостаточно средств, неверный токен, таймаут Stripe.
2. Использовать pytest fixtures, parametrize для граничных значений.
3. Названия тестов: test_<сценарий>_<ожидаемый_результат>.
4. Добавить assert на статус ответа, вызов мока, изменение БД.
5. Не использовать monkeypatch — только responses.
Вот код модуля:
[код]
Вот пример теста:
[пример]
Сгенерируй только код, без объяснений.
4 Запустите, отладьте, итерируйте
Первый результат почти всегда содержит ошибки. Скопируйте ошибку в новый промпт с просьбой исправить. Или поправьте вручную. Через 2-3 итерации тест становится рабочим. Не ждите идеала с первого раза.
5 Внедрите в CI/CD
Когда тесты готовы, добавьте их в пайплайн. Но сначала — код-ревью человеком. Мы используем правило: тест, сгенерированный ИИ, проходит такое же ревью, как и рукописный. Это снижает риск появления "мусорных" тестов, которые только увеличивают время прогона.
Уроки, которые мы выучили на своей шкуре
Урок 1: ИИ не понимает бизнес-контекста
Однажды LLM сгенерировал тест, который проверял, что после успешного платежа статус заказа меняется на "shipped". В реальности статус должен был стать "paid", а "shipped" выставляется только после отгрузки. Модель "додумала" логику. Мы потратили час на выяснение, что тест неправильный. Решение — всегда явно описывать ожидаемое поведение в промпте.
Урок 2: Размер контекста имеет значение
Современные модели поддерживают огромные окна (до 200k токенов). Но это не значит, что нужно пихать всё подряд. Мы заметили: чем больше контекста, тем выше вероятность, что модель "забудет" начало. Оптимальный объем — 10-15% от максимального окна. Структурируйте контекст: сначала самое важное (код), потом детали (схема БД), потом примеры.
Урок 3: Люди сопротивляются
Внедрение ИИ в тестирование — это не только техническая, но и социальная задача. QA-инженеры боятся, что их заменят. Разработчики не доверяют сгенерированным тестам. Мы прошли через это и написали отдельный материал: "Психология сопротивления ИИ: как работать с хейтерами". Кратко: покажите ценность на маленьком примере, обучите, дайте право голоса.
Урок 4: Безопасность и галлюцинации
LLM может сгенерировать тест, который использует несуществующие методы безопасности, или, того хуже, сохраняет чувствительные данные в логах. В нашем аудите галлюцинаций 2026 мы выяснили, что даже самые современные модели врут в 5-15% случаев. Для тестов это критично. Всегда проверяйте, что тест не использует секреты в открытом виде и не вызывает опасных операций (например, удаление данных).
Лучшие практики, которые мы наработали за 6 месяцев
- Используйте версионирование промптов. Сохраняйте удачные промпты в git или Wiki. Это позволяет быстро адаптировать их для новых модулей.
- Создайте библиотеку "золотых" тестов. 5-10 идеальных тестов, на которые LLM может ориентироваться как на пример стиля.
- Не генерируйте всё сразу. Лучше по одному тесту на сценарий, чем 50 тестов с одинаковыми ошибками.
- Интегрируйте объяснение решений. Используйте принципы Explainable AI, чтобы понимать, почему ИИ выбрал именно такой тест. Просите LLM комментировать свои решения.
- Не забывайте про сдвиг влево (shift-left). Генерация тестов на этапе проектирования (по спецификациям) эффективнее, чем по готовому коду. Мы начали генерировать тесты параллельно с написанием кода — это сократило цикл обратной связи.
Типичные ошибки новичков (и как их избежать)
- Ошибка: давать модели слишком мало контекста. Исправление: используйте шаблон контекст-пакета выше.
- Ошибка: верить первому результату. Исправление: всегда прогоняйте тест и проверяйте на баге.
- Ошибка: не проверять негативные сценарии. LLM по умолчанию пишет happy path. В промпте явно указывайте: "напиши также тесты на ошибки: network timeout, invalid input, authentication failure".
- Ошибка: забывать про производительность. Сгенерированные тесты могут быть медленными (например, использовать реальные вызовы API вместо моков). Указывайте в промпте требования к скорости.
- Ошибка: игнорировать человеческий фактор. Если команда не готова, пилот провалится. Проведите обучение, сделайте демо, покажите, что ИИ — помощник, а не замена. Почитайте нашу статью про психологию сопротивления.
Что дальше? Прогнозы на 2026-2027
Мы уже сейчас видим, как ИИ переходит от генерации отдельных тестов к автономному исследованию приложений. Модели вроде Claude 4 Opus умеют запускать приложение, кликать по кнопкам и записывать тесты. Но пока это экзотика. Основной тренд — интеграция LLM в IDE (как в Cursor или Copilot) с возможностью вызывать модель прямо из кода. Мы уже используем это для рефакторинга тестов.
Еще одно направление — RAG-системы, которые хранят документацию, требования и историю багов. Вместо того чтобы пихать всё в промпт, модель обращается к векторной базе. Это снижает галлюцинации и ускоряет генерацию. Пример такой архитектуры для Java мы описали в статье "RAG + LLM генерируют Java-тесты".
Но есть и риски. Чем больше мы полагаемся на ИИ, тем больше атрофируются наши навыки написания тестов. Статья про автопилот в кодинге хорошо объясняет этот эффект. Мой совет: продолжайте писать тесты руками хотя бы для новых сценариев. ИИ — для рутины, человек — для творчества.
Важно: не пытайтесь автоматизировать всё. Если модуль меняется каждый спринт, ИИ-тесты будут устаревать быстрее, чем вы их проверите. Используйте ИИ для стабильных компонентов.
Помните: цель ИИ в тестировании — не заменить QA, а дать им время думать о сложных кейсах, а не писать boilerplate. Если ваша команда сейчас тратит 70% времени на написание тестов, а 30% — на анализ, наша методика может перевернуть пропорцию. Попробуйте — и пишите в комментариях, что получилось.