golang

Пусть ваш AI пишет тесты. Имба, о которой не знает ни один вайбкодер

  • среда, 4 марта 2026 г. в 00:00:11
https://habr.com/ru/articles/1005732/

Вы попросили ChatGPT добавить скидку 10% на заказы больше $100. Он выдал 40 строк кода. Скидка работает - проверили на заказе в $150, получили $135. Закоммитили.

Чего вы не заметили: модель переписала функцию расчёта цены целиком. Не потому что нужно было. Просто так получилось - LLM не "редактирует" код, он генерирует новый текст на основе вашего промпта и контекста. В процессе он изменил порядок вычисления налога. Заказы меньше $100 теперь считают налог дважды. Товар за $10 стоит $10.80 вместо $10.50. Вы узнаете об этом через три недели, когда клиент напишет в поддержку.

Тест поймал бы это за полсекунды:

--- FAIL: TestCalculatePrice
    expected 1050 (cents), got 1080

Что вообще такое тест

Если совсем просто: тест - это записка для компьютера. В ней написано "вот это работает вот так, и если перестанет - дай знать."

У вас есть калькулятор. Записка говорит: "2 + 3 должно быть 5." Компьютер проверяет эту записку каждый раз, когда вы что-то меняете. Если калькулятор вдруг вернёт 6, записка срабатывает. Вам не нужно понимать, как калькулятор устроен внутри. Вы просто знаете, что он сломался, потому что 2 + 3 - это не 6.

В коде это выглядит примерно так:

func TestCalculatePrice(t *testing.T) {
    got := calculatePrice(10, 3)
    if got != 30 {
        t.Errorf("expected 30, got %d", got)
    }
}

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

"Юнит-тест" - потому что проверяет один маленький кусочек поведения. Не всё приложение. Не базу данных. Только: делает ли эта функция то, что должна.

Почему вайбкодеру нужны тесты больше, чем кому-либо

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

AI ничего не помнит.

Каждый промпт - это новый контекст. Модель не знает, что в прошлом диалоге она написала функцию, от которой зависят три других файла. Она не "решает" сломать вашу логику. Она просто генерирует код, который выглядит правильным в контексте текущего промпта. Компилируется? Да. Проходит беглый взгляд? Да. Работает правильно во всех случаях? Это уже как повезёт.

Без тестов вы играете в рулетку при каждом изменении. С тестами вы точно знаете, что сломалось.

Тесты не должны подглядывать в реализацию

Тут есть ловушка, в которую попадают даже опытные разработчики.

Допустим, у вас есть функция оформления заказа. Хороший тест: "даю три товара по $10, ожидаю итог $30." Плохой тест: "функция должна вызвать database.Save() ровно один раз, потом вызвать cache.Invalidate() с аргументом "orders"."

Второй вариант проверяет не ЧТО делает код, а КАК он это делает. Вроде бы подробнее. На практике - катастрофа. AI переписывает внутренности при каждом промпте. Переместил часть кода в другое место? Тесты падают. Поменял одну внутреннюю библиотеку на другую? Тесты падают. Функция делает ровно то же самое, но тесты этого не знают, потому что они следили за шестерёнками, а не за результатом.

Простое правило: если вы можете описать, что проверяет тест, не упоминая названия внутренних частей кода или порядок действий - это хороший тест. "Заказ из трёх товаров по $10 стоит $30." Это предложение ничего не говорит о реализации. Любой код, который делает его правдой - правильный код.

Сколько тестов писать

Честно - не уверен, что есть универсальный ответ. Стандартный совет "покрывайте всё" для вайбкодинга не очень практичен. Приоритет другой: тестируйте то, что больно сломать тихо. Расчёт цен. Авторизация. Данные, которые пишутся в базу.

Если AI сгенерировал вспомогательный код для отображения даты и он сломается - вы увидите это на экране. Если AI сгенерировал код, который решает, списать с пользователя $50 или $500 - напишите тест.

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

samurai

Если вы пишете на Go, samurai хорошо ложится на этот подход. Библиотека изначально проектировалась с прицелом на разработку с помощью AI: минимальный API (один метод s.Test()), который легко объяснить модели в промпте, и полная изоляция между тестами, чтобы AI-сгенерированный код с неожиданными побочными эффектами не ломал соседние проверки. Каждый тест - изолированный путь со своей подготовкой и очисткой. AI переписал ваш код - запускаете тесты, каждый путь либо проходит, либо падает сам по себе. Никаких каскадных поломок. Ноль зависимостей, параллельность по умолчанию: go get github.com/zerosixty/samurai.