TL;DR
Исследование показывает, что прошедший тест от агента почти ничего не гарантирует. Агенты (Claude Code, Codex CLI, OpenHands, Aider) получали требование на естественном языке и писали по нему тесты к проекту. Авторы проверяли тесты искусственными поломками кода: ловит ли тест те же поломки, что и тест разработчика. Это и есть мера полноты проверки.
Главная находка: зелёная галочка обманывает. У лучшей конфигурации 82,8% тестов проходят, но полноценными оказываются только 33,8%. Остальные запускаются и не падают, но пропускают поломки: проверяют мало, подгоняют ожидание под код или обходят суть требования. Покупка модели дороже почти не помогает: цена генерации выросла в 6 и 4,9 раза, а полнота проверки сдвинулась на +5,5 и −2,5 процентных пункта.
Причина в дыре между требованием и тестом. В 90–99% неполных тестов требование не уточняло, как подготовить данные (setup) или что именно должно получиться на выходе. Агент достраивает это сам, каждый раз по-разному. Поэтому лучший из трёх запусков оказывается полноценным на 8,6–10,2 п.п. чаще, чем единственный.
Схема оценки в исследовании
ВХОД АГЕНТУ: Требование (Feature + Scenario) + сигнатуры целевых методов
+ репозиторий БЕЗ реализации проверяемого поведения
ШАГ 1: Агент пишет полный тест (часто несколько файлов)
ШАГ 2: Тест запускают на настоящем коде → «проходит / не проходит»
ШАГ 3: В код вносят мелкие поломки (мутанты) → какие из них тест ловит?
ШАГ 4: Сравнение с тестом разработчика → «ловит всё, что ловит эталон?»
ИТОГ: Проходит ≠ полноценен (82,8% против 33,8%)
Пример применения
Выводы статьи про тесты и требования. Сильная зона: поведение описано как «дано / когда / тогда», и есть код, куда агент пишет проверки. Ниже промпт собран по формату входа из исследования с добавлением «упущенных» полей.
Задача: Вы ведёте интернет-магазин на Bitrix (или самописном бэкенде). Нужно покрыть тестами правило: «Промокод ВЕСНА10 даёт скидку 10% на заказ от 3000 ₽, не суммируется со скидкой постоянного клиента». Просите Claude Code написать тесты.
Промпт:
Ты пишешь автотесты в существующем проекте магазина. Реализацию проверяемого
правила не меняй и не смотри в неё: тесты должны проверять требование.
Промокоды в корзине
Дано: клиент с накопительной скидкой 5% и корзина на 4000 ₽
Когда: он применяет промокод ВЕСНА10
Тогда: скидка по промокоду не суммируется со скидкой клиента,
применяется большая из двух
Cart::applyPromo(string $code), Cart::getTotal(), Customer::getLoyaltyDiscount()
Данные для теста создавай через существующие фикстуры в tests/fixtures.
Если нужной фикстуры нет — перечисли, какие данные нужны, и не придумывай их молча.
Итоговая сумма: 3600 ₽ (скидка 10% как большая из двух).
Отдельно проверь граничные случаи: корзина ровно 3000 ₽ и 2999 ₽.
Сделай ТРИ независимых варианта теста с разными подходами к подготовке данных.
Для каждого варианта перечисли, какие поломки в коде он бы поймал
(например: убрали условие порога, сложили скидки, сравнение > вместо >=).
Выбери лучший вариант и объясни выбор.
Результат: Агент вернёт три варианта теста, для каждого список «какие поломки поймаем». Затем выберет один и обоснует выбор. Если фикстур не хватает, он назовёт недостающие данные вместо тихого выдумывания. Вы сразу увидите, что тест на самом деле проверяет, а не только то, что он зелёный.
Почему это работает
Слабость. Агент превращает требование в тест, достраивая недостающее сам: какие данные завести, какое значение считать правильным. Если требование об этом молчит, он угадывает. Угаданный тест легко проходит, потому что проверяет мало, и ошибки при этом не видно. Поэтому доля «проходит» велика, а доля «проверяет всё нужное» мала.
Сильная сторона. Агент хорошо пользуется заготовленным: фикстурами, помощниками, сигнатурами методов из проекта. А разные запуски дают разные тесты, и среди них находятся более полные. Эту случайность можно использовать.
Как это используется. Метод закрывает пробелы заранее: setup и ожидаемый результат прописаны явно, а лишняя случайность превращается в выбор из нескольких вариантов. Рычаги:
- Число вариантов. Три в исследовании дали +8–10 п.п. Для критичных мест можно больше.
- Поле . Чем точнее описаны данные, тем меньше домыслов.
- Поле . Конкретные значения и границы убирают «неявные» ожидания, главную причину неполных тестов.
- Список «какие поломки поймаем». Это вынуждает агента думать о проверке, а не о прохождении.
Шаблон промпта
Первые четыре блока повторяют вход агента в исследовании (требование, целевые интерфейсы, контекст проекта). Блоки и — практический вывод из анализа ошибок.
Ты пишешь автотесты в существующем проекте. Не меняй код проверяемого
поведения и не подгоняй тест под текущую реализацию: тест проверяет ТРЕБОВАНИЕ.
{название_и_описание_возможности}
Дано: {исходное_состояние}
Когда: {действие}
Тогда: {наблюдаемый_результат}
{методы_и_классы_через_которые_проверяем}
{где_лежат_фикстуры_хелперы_и_как_запускать_тесты}
{какие_данные_и_состояние_подготовить; если_не_хватает — перечисли_что_нужно,
не придумывай молча}
{конкретные_значения_и_границы_что_считать_правильным}
Сделай {число_вариантов} независимых варианта теста.
Для каждого перечисли, какие поломки кода он поймает.
Выбери лучший вариант и объясни выбор.
Что подставлять:
- {исходное_состояние}, {действие}, {наблюдаемый_результат} — требование по схеме «дано / когда / тогда».
- {методы_и_классы_через_которые_проверяем} — публичные методы, до которых дотягивается тест.
- {конкретные_значения_и_границы_что_считать_правильным} — числа, статусы, сообщения и граничные случаи.
- {число_вариантов} — 3 для обычных мест, больше для критичных.
🚀 Быстрый старт — вставь в чат:
Вот шаблон запроса на генерацию тестов из требования. Адаптируй под мою задачу:
[опиши, какое поведение нужно проверить и в каком проекте].
Задавай вопросы, чтобы заполнить поля.
[вставить шаблон выше]
LLM спросит про исходные данные, точные ожидаемые значения и границы, потому что именно эти пробелы исследование назвало причиной неполных тестов. Она возьмёт структуру из шаблона и соберёт готовый запрос.
Ограничения
⚠️ Узкая область: Исследование проверяло только генерацию тестов из требований в формате BDD (Gherkin) в 8 проектах. Для других типов задач прямого подтверждения нет.
⚠️ Эталон — тест разработчика: «Полный» тест здесь означает «ловит всё, что ловит тест разработчика». Эталон мог быть неидеальным, а мера строгая.
⚠️ Лучший из трёх — потенциал, а не готовый приём: Авторы показали, что выбор из нескольких вариантов помогает, но не дали рабочей схемы выбора. Чтобы выбрать лучший, нужен отдельный сигнал качества, например запуск с поломками кода.
⚠️ Явные Setup и ExpectedOutcome в шаблоне — вывод из анализа ошибок: Прямого эксперимента «до и после уточнения требований» в доступном тексте нет. Проверьте эффект на своих задачах.
⚠️ Настройка проверки: Чтобы реально ловить поломки кода, нужен инструмент мутационного тестирования или ручная работа.
Как исследовали
Исследователи собрали 5 086 задач из 8 реальных открытых проектов на 5 языках с 4 BDD-фреймворками. В этих проектах требования уже записаны по схеме «дано / когда / тогда», и к каждому есть тест разработчика. Агенту давали требование, список целевых методов и репозиторий без реализации проверяемого поведения (в жёстком варианте вообще без всей реализации). Агент должен был написать полный тест, нередко из нескольких файлов.
Дальше шла проверка. Тест запускали на настоящем коде, а потом на копиях кода с мелкими поломками: подмена > на >=, удаление вызова, замена возвращаемого значения. Всего 11 типов поломок. Смотрели, ловит ли сгенерированный тест те же поломки, что и тест разработчика.
Проверили 8 конфигураций: четыре агента (Aider, OpenHands, Claude Code, Codex CLI), четыре модели, разная глубина рассуждений. Удивило то, что дорогая модель не выигрывает. Переход от GPT-6 Luna к DeepSeek-V4.1-Flash и затем к GPT-6 Sol удорожал генерацию в 6 и 4,9 раза, а полнота проверки менялась 28,3% → 33,8% → 31,3%.
Разбор неполных, но проходящих тестов показал: в 90–99% случаев требование оставляло setup неясным или ожидаемый результат неявным. Агент добирал недостающее сам, и разные запуски давали разное. Отсюда третий вывод: лучший из трёх запусков полноценен на 8,6–10,2 п.п. чаще, чем один.
Адаптации и экстраполяции
Это мои идеи, не выводы статьи.
🔧 Техника: добавить «проверку на поломках» вручную → проверка качества без инструментов
После того как агент написал тест, попросите его сломать свой код:
Теперь внеси в проверяемый код 5 мелких поломок (замени > на >=, убери условие,
поменяй константу, удали вызов) по одной, запусти тест на каждой и покажи таблицу:
поломка → тест упал или нет. Для непойманных поломок допиши проверки.
Это упрощённая версия мутационной оценки из статьи. Она ловит тесты, которые «проходят, но ничего не проверяют».
🔧 Техника: переписать требование в виде «дано / когда / тогда» → меньше дыр
Если требование написано свободным текстом, сначала попросите модель переписать его в этом формате и перечислить, чего не хватает (данные, ожидаемые значения). Затем закройте пробелы сами и только потом генерируйте тесты.
Ресурсы
- Benchmarking LLM Agents for Behaviorally Adequate Test Generation from Software Requirements
- Авторы: Binhang Qi, Yiming Liu, Yun Lin, Yangjie Zhou, Hailong Sun, Jin Song Dong
- Организации: National University of Singapore; Shanghai Jiao Tong University; Shanghai Innovation Institute; Beihang University
- Код, данные и лидерборд Req2Test-Bench опубликованы авторами (ссылка в списке литературы [32])
- Связанные работы: E2EDev, GivenWhenThen, AutoGlue, DevBench, SWT-Bench
