Тестирование с ИИ: полная петля от теста к честному mutation score
Сцена · агент пишет, ты судишь
Агент пишет реализацию за секунды и тесты за секунды. Кажется, что проблема тестирования решена: попроси, и получишь сто зелёных тестов. Но скорость генерации бесполезна без критерия честности. Агент одинаково охотно напишет тест, который ловит баги, и тест-тавтологию, который не ловит ничего: оба зелёные, оба выглядят работой.
Эта серия с самого начала вела к одной развязке: “зелёный тест не значит хороший тест”. Теперь мы собираем все нити в один рабочий процесс. Тест как ТЗ для агента задаёт, что делать. Mutation testing проверяет, честно ли проверяет твой тест. Между ними петля, которая сходится к набору, которому можно верить.
Карта урока · что заберёшь
- соберёшь полный AI-workflow: тест, агент, Stryker, уточнение, повтор;
- освоишь промпт-стратегии: что просить у агента и что запрещать;
- научишься обнаруживать пустые ИИ-тесты по низкому mutation score и тавтологиям;
- поймёшь, почему mutation score это объективный судья над агентским набором.
Раздел 1 · полная петля
Идея словами
Workflow это не “сгенерируй тесты”, а замкнутый цикл с обратной связью: ты пишешь тест как спецификацию поведения, агент пишет реализацию, Stryker сажает мутантов и считает, сколько твои тесты убили, по выжившим мутантам ты усиливаешь тесты, и цикл повторяется, пока mutation score не выйдет на высокий уровень. Stryker тут не финальная проверка, а руль, который правит петлю.
Картинка · петля сходится
Виджет показывает связку “тест как ТЗ, агент реализует, red/green”. Под полной петлёй к ней добавляется шаг Stryker: после зелёного прогона мутанты показывают, что тест на самом деле не проверяет, и уточнение тащит mutation score вверх. Неоднозначное ТЗ даёт расходящиеся реализации, и петля их разводит.
Шаг 1 · тест как спецификация, агент как исполнитель
Начинаем не с реализации, а с таблицы случаев. Это центральный паттерн урока 11: человек формулирует поведение, агент пишет код. Берём validatePayment домена кассы:
// packages/ledger/test/validate-payment.test.ts
it.each([
{ amount: 30, balance: 100, valid: true },
{ amount: 100, balance: 100, valid: true }, // ровно остаток это ок
{ amount: 101, balance: 100, valid: false }, // сверх остатка
{ amount: 0, balance: 100, valid: false }, // ноль не платёж
{ amount: -5, balance: 100, valid: false }, // отрицательное
])('amount=$amount при balance=$balance это valid=$valid', ({ amount, balance, valid }) => {
const result = validatePayment(makeAccount({ balance }), makePayment({ amount }));
expect(result.valid).toBe(valid);
});
Эта таблица и есть ТЗ. Агент по ней пишет validatePayment. Зелёный прогон значит “реализация совпала с примерами”. Но совпала ли она с поведением, или просто прошла пять конкретных чисел, ещё неизвестно.
Шаг 2 · Stryker правит петлю
Запускаем mutation testing на домене:
pnpm --filter kassa-ledger exec stryker run
Stryker сажает мутантов: меняет > на >=, amount > 0 на amount >= 0, убирает проверки. Если граница “ровно остаток это ок” не зафиксирована тестом, мутант balance >= amount против balance > amount выживет. Выживший мутант это адрес дыры: тест не проверяет ровно эту границу. Дописываешь случай, мутант умирает, mutation score растёт.
итерация | покрытие | mutation score | выжившие
─────────┼──────────┼────────────────┼──────────────────────────
1 | 100% | 62% | граница "ровно остаток", ноль
2 | 100% | 85% | знак отрицательного
3 | 100% | 100% | (только неинтересные в текстах)
Покрытие всё время 100, а mutation score ползёт с 62 до 100. Вот доказательство, что coverage обманывает, а mutation честен: строки исполнялись с первой итерации, но поведение на границах проверилось только к третьей.
Что взять с собой
- workflow это замкнутая петля: тест, агент, Stryker, уточнение, повтор;
- Stryker это руль петли, а не финальная печать: выживший мутант это адрес недостающего случая;
- петля сходится, когда mutation score выходит на высокий уровень при стабильном покрытии.
дальше после входа
Урок целиком открыт всем, кто вошёл. Войди по своему email, чтобы читать дальше.
Вход пока по приглашению. Если хочешь учиться, напиши Vassiliy.