Контракты вместо ассертов: как тестировать код, написанный ИИ

Автор серии статей о создании игрового сервера на PHP столкнулся с проблемой: половину контрактов нельзя проверить юнит-тестами. Решение — таблицы «дано → действие → ожидание», которые прогоняет ИИ-агент. Разбираем, как устроен этот контур проверки и почему он может стать стандартом для разработки с генеративными моделями.

Контракты вместо ассертов: как тестировать код, написанный ИИ

Когда код пишет не человек, а нейросеть, классические подходы к тестированию дают сбой. Разработчик из Хабра, создающий онлайн-ММО на PHP, обнаружил, что половину контрактов его игрового сервера невозможно записать ассертами: результат существует в ответе живого процесса, в разметке страницы и в кадре игры. В двадцатой части серии он рассказывает, как заменил юнит-тесты на контракты, которые прогоняет ИИ-агент. Разбираемся, чем эта схема отличается от привычного TDD и почему она может стать стандартом для разработки с генеративными моделями.

Тесты для кода, который пишет ИИ: контракты вместо ассертов

Автор статьи ведёт цикл о создании сервера для онлайн-ММО на PHP. В прошлых частях он использовал ассерты для проверки бизнес-логики, но с ростом проекта столкнулся с ограничением: часть контрактов невозможно выразить в виде утверждений внутри кода. Например, как проверить, что игровой кадр содержит правильные спрайты, или что ответ сервера содержит корректную HTML-разметку? Традиционные юнит-тесты здесь бесполезны.

Решение, которое он предлагает, — контракты, записанные в виде таблиц «дано → действие → ожидание». Каждая строка таблицы описывает конкретный сценарий: начальное состояние системы, действие пользователя или процесса, и ожидаемый результат. Эти таблицы хранятся отдельно от кода и прогоняются ИИ-агентом, который выполняет действия в реальной среде и проверяет соответствие ожиданиям. Такой подход позволяет тестировать то, что невозможно проверить статически.

Ключевая особенность — агент не просто выполняет сценарий, но и анализирует результат. Если ожидание не совпало с реальностью, агент фиксирует расхождение и формирует отчёт. Разработчик получает не просто «тест упал», а подробное описание того, что пошло не так. Это экономит время на отладку, особенно когда код написан нейросетью и его поведение не всегда очевидно.

Предыстория и контекст

Проблема тестирования кода, созданного ИИ, становится всё актуальнее. Генеративные модели, такие как GPT-4 и Claude, активно используются для написания кода, но их выход непредсказуем. Классические юнит-тесты, которые пишутся до кода, здесь не работают: разработчик не всегда знает, как именно модель реализует функцию. Поэтому подходы вроде property-based testing или контрактного тестирования выходят на первый план.

Автор статьи уже использовал контракты в предыдущих частях цикла, но теперь он формализовал их в виде таблиц и подключил ИИ-агента для автоматического прогона. Это шаг в сторону более надёжной разработки с ИИ: вместо того чтобы полагаться на ручную проверку, разработчик создаёт формальное описание ожидаемого поведения, а агент проверяет его выполнение.

Интересно, что автор не отказывается от ассертов полностью. Он отмечает, что половину контрактов можно проверить статически, и это по-прежнему эффективно. Но для остальных — тех, что живут в ответах живых процессов, — нужен другой инструмент. Его решение — гибридный подход, где статические проверки сочетаются с динамическими, выполняемыми агентом.

Как это работает?

Суть метода проста. Разработчик описывает контракт в виде таблицы: в колонке «дано» указывается начальное состояние (например, залогиненный пользователь с определёнными правами), в колонке «действие» — что нужно сделать (отправить запрос на сервер), в колонке «ожидание» — что должно произойти (получить ответ с кодом 200 и JSON-объектом). Таблицы хранятся в отдельном файле, например в формате Markdown или YAML.

Затем запускается ИИ-агент, который читает таблицу, выполняет действия в реальной среде (например, отправляет HTTP-запросы к запущенному серверу) и сравнивает результат с ожиданием. Если совпадение — тест пройден, если нет — агент генерирует отчёт с деталями. Агент может быть написан на любом языке, но автор использует PHP-скрипт, который взаимодействует с внешним ИИ (например, через API) для анализа сложных результатов.

Важно, что агент не просто механически сверяет значения, а умеет интерпретировать их. Например, если ожидается, что на странице есть кнопка с текстом «Купить», агент может проверить это через DOM-парсинг. Если ожидается, что кадр игры содержит определённые объекты, агент может использовать компьютерное зрение. Это делает контракты гораздо более гибкими, чем ассерты.

Технические подробности: как устроен контур проверки

Автор делится деталями реализации. Он использует PHP-скрипт, который читает таблицы контрактов, запускает агента и собирает результаты. Агент работает в цикле: для каждого контракта он выполняет предусловия, запускает действие, ожидает результат и проверяет его. Если результат не совпал, агент пытается понять причину, анализируя логи или состояние системы, и формирует человекочитаемое описание.

Одной из проблем, с которой столкнулся автор, была нестабильность среды: тесты зависят от внешних факторов, таких как состояние базы данных или сетевые задержки. Чтобы минимизировать флаки, он вводит понятие «границы контракта»: контракт считается выполненным, если результат соответствует ожиданию с определённой точностью. Например, время ответа может варьироваться, но должно быть в пределах разумного.

Ещё одна важная деталь — ревью. Автор отмечает, что ИИ-агент не полностью доверяет: находку надо доказать. Если агент сообщает об ошибке, разработчик может запросить доказательства: логи, скриншоты, трассировки. Это повышает доверие к автоматизированной проверке и снижает риск ложных срабатываний.

Автор также подчёркивает, что есть граница, за которую машину не пускают. Некоторые контракты требуют человеческого суждения: например, оценка юзабилити или визуальной привлекательности. Такие проверки остаются за рамками автоматизации, и это правильно.

Кого затронет и как

Этот подход будет полезен всем, кто разрабатывает с использованием ИИ-генерации кода. Если вы используете ChatGPT или Copilot для написания функций, вы наверняка сталкивались с ситуацией, когда код работает, но не совсем так, как ожидалось. Контракты позволяют формализовать ожидания и автоматически проверять их, что ускоряет разработку и повышает качество.

Особенно актуально для веб-разработки, где многое зависит от интеграции с внешними сервисами и от динамического поведения страниц. Ассерты не могут проверить, что страница рендерится правильно в браузере, но агент с доступом к браузеру может. Это делает контракты мощным инструментом для E2E-тестирования.

Для русскоязычного сообщества разработчиков этот материал интересен тем, что показывает практический опыт применения контрактного тестирования в реальном проекте. Многие статьи на эту тему носят теоретический характер, здесь же — конкретный код, таблицы и результаты.

Что будет дальше

Автор планирует продолжить серию и, вероятно, расширит контур проверки. Возможно, он добавит поддержку других языков, кроме PHP, или интеграцию с CI/CD. Также он может поделиться шаблонами таблиц контрактов, чтобы другие разработчики могли адаптировать их под свои проекты.

В более широком смысле, развитие таких подходов может привести к появлению стандартов контрактного тестирования для ИИ-генерируемого кода. Уже сейчас существуют инструменты вроде Pact для контрактного тестирования API, но они не учитывают специфику ИИ. Возможно, в будущем мы увидим фреймворки, которые автоматически генерируют контракты из кода, написанного нейросетью.

Итог

Контракты вместо ассертов — это не просто замена одного инструмента другим, а смена парадигмы тестирования в эпоху ИИ. Когда код пишет машина, разработчик должен описывать не алгоритмы, а ожидаемое поведение. Таблицы «дано → действие → ожидание» — простой и эффективный способ сделать это. Если вы работаете с ИИ-генерацией кода, стоит присмотреться к этому методу: он может сэкономить вам часы отладки и сделать ваши тесты более надёжными.