Ассистент пишет тесты быстро и охотно. Это одновременно его сильная сторона и главная ловушка. Тест ценен не количеством строк, а тем, что он падает, когда код сломан. Сгенерированный тест легко получается зелёным и бесполезным.
Тесты первыми
Самый надёжный приём: сначала зафиксировать поведение, потом просить реализацию.
- Вы описываете требования и краевые случаи сами или вместе с ассистентом.
- Пишете или генерируете тесты и читаете их как спецификацию: это то, что вы хотите?
- Запускаете тесты и убеждаетесь, что они падают. Тест, который зелёный без реализации, ничего не проверяет.
- Просите ассистента написать код, который проходит тесты, не меняя сами тесты.
Так у вас есть внешний критерий готовности. Ассистент не может «договориться сам с собой»: если он пишет и код, и тесты по одному и тому же пониманию, ошибка в понимании попадёт в оба места и тесты её не поймают.
Ловушки сгенерированных тестов
- Тавтология. Ожидаемое значение вычисляется тем же кодом, что и проверяемое:
expect(sum(a, b)).toBe(sum(a, b)). Или ожидание просто скопировано из текущего вывода функции, включая баг. - Моки всего. Замокано всё, включая проверяемую логику. Тест проверяет, что мок вернул то, что вы в него положили.
- Только счастливый путь. Нет пустого ввода, нуля, отрицательных чисел, границ диапазона, ошибок сети, таймзон.
- Проверка реализации, а не поведения. Тест проверяет, что вызван приватный метод с такими аргументами. Любой рефакторинг его ломает, хотя поведение верное.
- Слабые утверждения.
expect(result).toBeDefined()илиtoBeTruthy()там, где нужно точное значение. - Тест подогнан под код. Тест падал, ассистента попросили «починить», и он поменял ожидание, а не код. Всегда смотрите, что именно изменилось в диффе.
Покрытие не равно качеству
Ассистент может за минуту поднять покрытие строк до высоких значений. Покрытие говорит только, что строка выполнилась, а не что результат проверили. Тест без единого утверждения тоже даёт покрытие.
Лучший способ проверить силу тестов: мутационное тестирование. Инструмент вносит мелкие изменения в код (меняет > на >=, + на -, удаляет вызов) и смотрит, упадут ли тесты. Выжившая мутация значит, что этот кусок поведения не проверен. Есть инструменты для большинства языков. Даже без инструмента можно руками: сломайте строку и посмотрите, покраснеет ли тест.
Где ИИ в тестах реально помогает
- Перечислить краевые случаи для функции: часто вспоминает то, что вы забыли.
- Сгенерировать табличные тесты по уже утверждённому списку случаев.
- Набросать тестовые данные и фабрики.
- Написать тест, воспроизводящий баг по описанию из тикета, до исправления.
- Перевести тесты с одного фреймворка на другой.
- Объяснить, почему падает тест, по выводу и стектрейсу.
Как просить тесты
Плохо: «напиши тесты для этого файла». Получите много зелёных тестов на счастливый путь.
Лучше:
Напиши тесты на parseAmount(input: string): number.
Требования: принимает "1 234,50" и "1234.5", пробелы игнорирует,
пустая строка и буквы бросают InvalidAmount, отрицательные запрещены.
Без моков, без вызова реализации для расчёта ожиданий.
Сначала выведи список случаев, код потом.
Список случаев вы проверяете глазами за минуту. Это дешевле, чем читать сто строк кода тестов.
Снапшоты и флаки
Снапшот-тесты ассистент генерирует особенно легко, и они же легко превращаются в шум: при любом изменении снапшот просто перезаписывают. Используйте их для стабильного вывода и смотрите дифф снапшота как код.
Если сгенерированный тест иногда падает, причина обычно в зависимости от времени, порядка, случайности или сети. Не просите ассистента «добавить ретрай»: попросите найти источник недетерминированности.
Что сказать на собесе
Тесты от ИИ я читаю как спецификацию и проверяю, что они падают на сломанном коде. Код и тесты от одного и того же понимания задачи друг друга не проверяют, поэтому критерий готовности фиксирую до реализации.