ИИ в тестировании кода

Почему зелёные unit-тесты ещё ничего не гарантируют?

Смотрит в светлое будущее
Смотрит в светлое будущее

Нейросеть написала шесть unit-тестов, pytest сообщил 6 passed. Значит ли это, что ошибок нет? Не обязательно. Если тесты воспроизводят неверное представление модели о работе функции, они могут пройти и при реальном баге.

Особенно легко так происходит, когда ИИ видит реализацию, но не видит требований. Разберём на примере, как генерация тестов превращается из проверки в закрепление ошибки — и как этого избежать.

Начнём с бага, который легко пропустить

Представим сервис с лимитом в десять одновременных задач. can_enqueue() получает число активных задач и количество новых. Добавлять можно, пока сумма не превышает лимит. Нулевое или отрицательное количество новых задач и отрицательное число активных считаются ошибкой.

Реализация выглядит вполне убедительно:

queue_limits.py def can_enqueue(active: int, incoming: int, limit: int = 10) -> bool: if active < 0 or incoming <= 0 or limit <= 0: raise ValueError("invalid queue parameters") return active + incoming < limit

Код выглядит правдоподобно, но в нём ошибка: ровно десять задач тоже разрешены. Вместо < нужно <=. Тесты на восемь и десять активных задач при добавлении одной могут оказаться зелёными, так и не проверив саму границу.

Добавим в запрос к ИИ контракт: «Общее число задач не должно превышать лимит; равенство разрешено». Теперь тесты можно строить независимо от реализации:

test_queue_limits.py import pytest from queue_limits import can_enqueue @pytest.mark.parametrize("active,incoming,expected", [ (0, 1, True), (8, 1, True), (8, 2, True), # Ровно десять задач: разрешено (9, 1, True), # Ещё одна проверка границы (10, 1, False), (0, 11, False), ]) def test_can_enqueue(active, incoming, expected): assert can_enqueue(active, incoming) is expected @pytest.mark.parametrize("active,incoming,limit", [ (-1, 1, 10), (1, 0, 10), (1, -1, 10), (1, 1, 0), ]) def test_invalid_parameters(active, incoming, limit): with pytest.raises(ValueError): can_enqueue(active, incoming, limit)

Команда python -m pytest -q обнаружит дефект в случаях (8, 2) и (9, 1): функция возвращает False вместо True. После замены < на <= проверки проходят. Параметризация позволяет видеть каждый случай отдельно; её синтаксис описан в документации pytest.

Генерация unit-тестов должна опираться на независимый источник ожидаемого результата. ИИ может придумать входные значения, но не должен изобретать бизнес-правила.

ИИ в тестировании кода: для чего он действительно нужен

Здесь LLM, AI-ассистенты и AI-IDE применяются для анализа функций, генерации проверок и исследования ошибок. Если ИИ для программирования пишет реализацию, то при тестировании он должен искать условия, когда она нарушает контракт.

Поэтому использование ИИ в тестировании начинается с описания входов, выходов, исключений и ограничений. Тест может быть синтаксически идеальным, но проверять неверную бизнес-логику.

Где генерация unit-тестов экономит больше всего времени

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

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

AI-IDE может учитывать импорты, соседние тесты и структуру репозитория. Но даже инструменты с контекстом проекта не узнают продуктовые правила автоматически, если их нигде нет.

Генерация unit-тестов: рабочая последовательность вместо одной команды

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

2. Передать технический контекст. Укажите язык, версии, фреймворк (pytest, Jest, JUnit), способ импорта и соглашения команды. Для сложного репозитория полезен контекст проекта: соседние файлы и тесты.

3. Получить матрицу сценариев до кода. Попросите таблицу «вход → ожидаемый выход → требование». Сначала проверьте предложенные позитивные, негативные и граничные случаи.

4. Создать минимальные тесты. Оставьте ценные сценарии. Повторяющиеся случаи объедините параметризацией, внешние зависимости изолируйте mocks.

5. Запустить тесты. Падение может сигнализировать о дефекте продукта, теста или окружения. А passed говорит лишь о выполнении конкретного assertion.

6. Проверить чувствительность. Временно замените <= на < и убедитесь, что граничный тест упал. Для системной проверки подобной чувствительности существует mutation testing.

Три промпта для разных задач: тесты, edge cases и падающий pytest

Запросу нужны контекст, цель, формат результата и ограничения самостоятельности модели.

1. Написать unit-тесты

Слабый запрос: «Сделай тесты для функции».

Сильный:

Проект на Python 3.12, тесты на pytest. Ниже can_enqueue() и контракт: суммарное число задач не превышает лимит, равенство допустимо. Неположительное количество новых задач, отрицательное число активных и неположительный лимит вызывают ValueError. Сначала составь матрицу позитивных, негативных и граничных случаев. Затем создай параметризованные тесты в test_queue_limits.py. Не меняй реализацию; укажи, какое требование проверяет каждый тест. Недостающие правила не выдумывай.

2. Найти слабые места в логике до написания тестов

Слабый запрос: «Найди баги в коде».

Сильный:

Ниже TypeScript-обработчик заказа, контракт API и правила идемпотентности. Найди edge cases: два одинаковых запроса, параллельные запросы, истёкший ключ, таймаут платежа, пустая корзина. Верни таблицу «условие → риск → ожидаемое поведение по контракту → тест». Пробелы спецификации пометь вопросами; код не меняй.

3. Разобраться с падающим тестом

Слабый запрос: «Тест красный, почини».

Сильный:

Вот код, тест, полный stack trace, версии зависимостей и описание правильного поведения. Предложи до трёх причин падения и способ проверки каждой. Раздели дефект реализации, ошибку теста и сбой окружения. Не меняй assertion ради зелёного статуса. Если предлагаешь патч, объясни связь с контрактом.

ИИ в автоматизации тестирования: что делать кроме написания assert

Нейросети в тестировании полезны не только для написания файла с тестами:

Поиск edge cases. Необычные сочетания условий, границы интервалов, пустые значения, null, дубли и переходы между часовыми поясами часто важнее «счастливого пути». Для денежных расчётов стоит отдельно проверить округление у порога скидки.

Анализ ошибок. Модель сопоставляет assert, код и stack trace и предлагает гипотезы. Причину подтверждают только воспроизведение, исправление и повторный запуск. Совет просто изменить expected может скрыть дефект.

Mocks, fixtures и данные. Mock заменяет внешний API предсказуемым ответом; fixture создаёт повторяемое окружение. ИИ может подготовить оба, но их нужно сверять с реальными контрактами. Заглушка не проверяет, что интеграция с внешней системой работает.

Регрессии и CI/CD. Модель способна подготовить тест на исправленный баг и команду запуска. Агент может выполнить pytest, прочитать падение и предложить diff. Агенты ИИ в тестировании удобны для цепочек действий, но изменения требуют ревью.

AI в тестировании не означает полную автономность: она ограничена доступными инструментами и качеством требований. Если модель не знает, в каком случае платёж нужно отклонить, запуск тестов сам по себе не подскажет ей правильную бизнес-логику.

Почему десять прошедших тестов могут быть хуже двух хороших

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

Хорошая проверка должна иметь понятную «причину падения»: конкретное нарушенное требование. Если такую причину нельзя сформулировать, добавление теста ради числа в отчёте по покрытию вряд ли поможет.

Вымышленный API. Модель импортирует несуществующий метод или передаёт аргумент из другой версии библиотеки. Обнаруживается при запуске в реальном окружении.

Избыточные mocks. Если замокать внутреннюю логику, тест проверит ответы заглушек. При этом он может сломаться от безобидного рефакторинга.

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

Агент «чинит» тест до зелёного статуса. Он может ослабить assert или сделать skip, не исправив баг. Без ревью это опаснее обычной неудачной генерации.

Что можно отправлять модели, если репозиторий закрытый

Не отправляйте внешней модели пароли, API-ключи, токены, персональные данные, приватные конфиги и реальные пользовательские записи. Они могут присутствовать и в логах или stack trace. Проверьте политику компании, условия обработки запросов и права агента на чтение файлов, изменение кода и запуск команд.

Маскирование чувствительных фрагментов до отправки снижает риски: например, для этого существует KodikShield. Но оно не заменяет ограничение доступов.

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

Чек-лист перед тем, как принять тесты от нейросети

  1. Ожидания взяты из требований? Не из текущей реализации?
  2. Проверены границы и ошибки? Не только обычные значения?
  3. Тест запускается в проекте? Реальные импорты, версии и fixtures совпадают?
  4. Mocks не заменили сам предмет проверки?
  5. Тест ловит дефект? Попробуйте воспроизвести старую ошибку или изменить граничное условие.
  6. Diff понятен? Агент не удалил проверки и не ослабил assertions?

ИИ в тестировании кода: можно ли полностью доверить ему проверку?

ИИ ускоряет перебор сценариев, написание проверок и анализ падений. Но бизнес-правила и приёмка тестов остаются за разработчиком: модель не знает того, чего нет в её контексте.

А если модель сама запускает pytest и исправляет код?

Это полезнее, чем генерация файла вслепую: агент получает фактические ошибки и может пройти несколько итераций. Но успешный запуск не доказывает, что бизнес-логика верна. Если задача сформулирована как «добейся зелёного pytest», агенту проще ослабить тест, чем найти нарушение требований. Поэтому обязательно смотреть diff и причину исправления.

Нужно ли вводить ИИ, если тестов пока нет совсем?

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

Вместо вывода

Unit-тесты нужны не для зелёного отчёта, а для сигнала о нарушенном контракте. ИИ снимает рутину — предлагает сценарии, пишет параметризацию, готовит fixtures, объясняет падения. С той же скоростью он может закрепить ложное предположение.

Поэтому лучший запрос к модели — не «напиши побольше тестов». Лучше и полезнее будет спросить: «Какие мои предположения о коде могут оказаться неверными — и каким тестом это проверить?»