Главная проблема генерации кода языковыми моделями — "уверенные галлюцинации", когда код выглядит корректно, но не выполняет требуемую бизнес-задачу. В контексте тестирования это приводит к появлению тавтологических тестов: тестов, которые проверяют сами себя, тестируют моки вместо реальной реализации или используют предельно широкие утверждения (assertions).
Для надежной интеграции ИИ в пайплайн требуется автоматизированная система верификации тестов. Mutation Testing (мутационное тестирование) является архитектурным стандартом для оценки качества тестов, генерируемых агентами. Оно проверяет, способны ли тесты "упасть", если бизнес-логика сломана.
Mutation Testing гарантирует, что ИИ не написал "пустые" тесты.
[Исходный код от ИИ] ─(Мутатор)─► [Мутант 1 (if a > b)] ──► [Тесты] ─► Успех (Мутант ВЫЖИЛ - Тест плохой)
│
├─► [Мутант 2 (if a < b)] ──► [Тесты] ─► Провал (Мутант УБИТ - Тест хороший)ИИ-агент пишет тесты по спецификации.
Линтеры запрещают использование слабых проверок (например, truthy).
Stryker вносит изменения в код реализации. Если тесты не падают (мутанты выживают), тесты признаются тавтологическими.
Отчет Stryker передается обратно агенту для усиления тестов.
Почему показатель 100% покрытия строк кода (Code Coverage) не гарантирует отсутствие галлюцинаций в тестах, сгенерированных AI-агентом?
Пример использования Stryker с TypeScript. Предположим, агент сгенерировал функцию и "тавтологический" тест к ней.
// Код, сгенерированный агентом
export function isValidPassword(password: string): boolean {
if (password.length >= 8) {
return true;
}
return false;
}import { expect, test } from "vitest";
import { isValidPassword } from "./password";
// Плохой тест: проверяет только одно граничное условие
test("пароль должен быть валидным", () => {
expect(isValidPassword("12345678")).toBe(true);
});Если запустить Stryker (npx stryker run), он изменит password.length >= 8 на password.length > 8 (или false / true). Тест упадет только в некоторых случаях, а мутант, заменяющий >= на >, выживет, потому что тест проверяет ровно длину 8. Это сигнал агенту (или инженеру), что тесты не покрывают граничные условия должным образом.
Какая архитектурная ошибка чаще всего возникает, когда AI-агент пытается самостоятельно сгенерировать юнит-тесты для сервиса оплаты?
Агент создает мок для тестируемой функции и проверяет, что мок возвращает нужное значение. Это классическая галлюцинация: тест не касается реального кода.
Использование expect(result).toBeTruthy(). Если функция вернет пустую строку вместо false, тест может упасть, а может и нет. Агенты должны использовать строгое сравнение toBe(true) или глубокое toStrictEqual.
Агент часто пишет тесты только для успешных сценариев. Если система должна выбросить исключение (например, ZodError при валидации), это обязательно должно проверяться через expect(() => fn()).toThrow().