Просьба «напиши тесты» часто приводит к проверкам, которые просто повторяют реализацию. Такие тесты проходят, но плохо защищают бизнес-правила. Чтобы Codex написал полезные unit-тесты вместе с кодом, нужно описать ожидаемое поведение, границы и ошибки до того, как смотреть на внутреннее устройство функции.

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

Начните с правила на обычном языке

Пусть доставка стоит 300 рублей, а при сумме заказа от 5000 рублей становится бесплатной. Отрицательная сумма недопустима. Для этого примера считаем сумму целым количеством рублей; реальная платежная логика может требовать целых копеек и отдельного правила округления.

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

Матрица сценариев

  • Заказ на 0 рублей: доставка 300 рублей, если это допустимый по требованиям расчет.
  • Заказ на 4999 рублей: доставка 300 рублей.
  • Заказ ровно на 5000 рублей: доставка бесплатная.
  • Заказ на 5001 рубль: доставка бесплатная.
  • Отрицательная сумма: согласованная ошибка валидации.

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

Запрос для Codex

Добавь функцию расчета доставки и unit-тесты.
Правила: 300 рублей, бесплатно от 5000 включительно.
Отрицательные значения отклоняются.
Сумма задана целым количеством рублей.
Используй существующий тестовый фреймворк проекта.
Проверь значения 0, 4999, 5000, 5001 и отрицательный вход.
Ожидаемые значения бери из требований, не из функции.
Не подключай сеть, базу и новый фреймворк.
Запусти тесты и сообщи фактический результат.
При неоднозначности входов сначала обозначь вопрос.

Фреймворк выбирают по проекту, а не по универсальному примеру из интернета. Официальная документация Codex рекомендует указывать конкретную функцию и придерживаться существующих соглашений тестов. Остальная полезность проверки зависит от качества требований.

Не вычисляйте ожидание тем же способом

Если тест вызывает ту же функцию для получения ожидаемого значения, он почти ничего не проверяет. Сходная проблема возникает, когда в тест буквально копируют алгоритм из реализации: одинаковая ошибка может присутствовать в обоих местах.

Ожидаемые результаты для простых примеров лучше задавать явно. Для сложных вычислений используйте независимо выведенные контрольные примеры или согласованные свойства. Например, изменение имени клиента не должно влиять на стоимость доставки, если имя не участвует в правиле. Но такое свойство тоже должно следовать из требований.

Где нужны моки

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

Не подменяйте моками всю внутреннюю логику, которую собираетесь проверять. Тест, где все вызываемые функции уже возвращают правильный результат по настройке, может подтверждать только корректность самой настройки. Интеграцию с настоящим хранилищем проверяют отдельно в безопасном окружении.

Добавляйте тесты вместе с изменением

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

Не запускайте сетевые операции и реальные платежи внутри unit-тестов. Данные должны быть искусственными, окружение воспроизводимым, а результат не зависеть от текущей даты или порядка запуска. Если время важно для правила, задайте его явно через принятую в проекте точку подмены.

Как проверить, что тест способен поймать ошибку

Посмотрите, что произойдет при замене условия «от 5000 включительно» на «больше 5000». Проверка на ровно 5000 должна упасть. Для такой оценки можно использовать отдельную копию или контролируемое временное изменение с восстановлением, не затрагивая чужие правки. Более системный подход называется мутационным тестированием, но для маленькой задачи достаточно осмысленного контрольного примера.

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

Что запросить в отчете

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

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

Также полезны материалы о критериях готовности и об исправлении ошибок. Если приложению не хватает проверок, начнем с критичных бизнес-сценариев, а не с формальной гонки за покрытием.