Amiga
Оглавление курса · урок 9 из 13

Модуль 2 · Строим · урок 9 из 13

Тесты как страховочная сетка

В прошлых уроках мы много раз говорили «это страхуют тесты». Пора проверить, что страховка держит. Тесты — главный механизм, который позволяет вайбкодить без чтения каждой строки. Но тесты, написанные тем же агентом, легко превращаются в зеркало: они проверяют, что код делает то, что делает, а не то, что нужно.

Что проверять на каком уровне

Для «Переговорок» сетка выглядит так:

Уровень Что проверяет Пример
Юнит Чистые функции и правила R1: слот кратен 15 минутам; границы дня по Москве
Интеграционный API вместе с настоящей базой S2: пересечение → 409; два параллельных запроса; отмена чужой брони → 403
E2E Сценарий пользователя в браузере вход по ссылке из письма, бронь, отмена

Интеграционные тесты — с настоящим PostgreSQL, а не с моком. Правило R3 живёт в ограничении базы, и мок его просто не увидит. Агент, которому сказали «напиши тесты», по умолчанию часто мокает слой данных — потому что так быстрее. Запишите в AGENTS.md: «Интеграционные тесты ходят в тестовую базу rooms_test, слой данных не мокать».

E2E-тестов немного, только ключевые сценарии. Вход по почте проверяем по-настоящему — ссылку берём из Mailpit через его API:

typescript · apps/web/e2e/booking.spec.ts
import { test, expect } from "@playwright/test";

const MAILPIT = process.env.MAILPIT_URL ?? "http://localhost:8025";

test("S1: сотрудник входит по ссылке и бронирует слот", async ({ page, request }) => {
  await page.goto("/login");
  await page.getByLabel("Рабочая почта").fill("anna@example.test");
  await page.getByRole("button", { name: "Прислать ссылку" }).click();

  const mail = await (await request.get(`${MAILPIT}/api/v1/message/latest`)).json();
  const link = mail.Text.match(/https?:\/\/\S+\/auth\/verify\S+/)[0];
  await page.goto(link);

  await page.goto("/rooms/1/week?date=2026-10-19");
  await page.getByRole("gridcell", { name: "Вторник 14:00" }).click();
  await page.getByLabel("Название").fill("Ретро спринта");
  await page.getByRole("button", { name: "Забронировать" }).click();

  await expect(page.getByRole("button", { name: /Ретро спринта/ })).toBeVisible();
});

Сначала красный

Саймон Уиллисон в гайде Agentic Engineering Patterns выделяет для агентов красно-зелёный цикл: сначала тест, который падает, потом код, после которого он проходит. Падение важно: тест, который ни разу не был красным, мог ничего не проверять.

text
Правило R4: отменить можно свою бронь, офис-менеджер — любую.
1. Напиши интеграционные тесты: своя отмена → 200, чужая → 403,
   офис-менеджер отменяет чужую → 200, отмена отменённой → 409.
2. Запусти и покажи, что тесты падают, и почему именно.
3. Остановись и жди моего подтверждения.

Пауза между шагами — момент, когда вы читаете тесты. Это те самые несколько десятков строк, которые курс требует читать всегда: если тест проверяет не то, весь зелёный прогон ничего не стоит. Подробнее о TDD с агентом — в уроке курса «Codex в ежедневной разработке».

Ещё один приём из того же гайда — начинать сессию с прогона тестов. Агент сразу узнаёт, как они запускаются и что уже сломано, и не примет чужую поломку за свою.

Как агент обходит тесты

Когда тест не проходит, у агента два пути: починить код или «починить» тест. Второй иногда короче, и модели им пользуются. Признаки:

  • изменились ожидаемые значения в существующих тестах;
  • появились .skip, .only или закомментированные проверки;
  • замокан именно тот модуль, который тест должен проверять;
  • try/catch, который глотает ошибку, чтобы тест «не падал»;
  • проверка заменена на более слабую: toBeDefined() вместо конкретного значения.

Правило в AGENTS.md («не править существующие тесты, чтобы они прошли — сообщи о конфликте») помогает, но проверять всё равно надо. Быстрый способ увидеть, трогал ли агент тесты:

bash
git diff --stat main -- '*.test.ts' '*.spec.ts'
git diff main -- '*.test.ts' '*.spec.ts' | grep -E '^\+.*\.(skip|only)\('

Если в задаче «добавь отмену брони» изменились старые тесты — это повод разобраться до коммита.

Проверить, что тест ловит ошибку

Зелёный тест доказывает только то, что он проходит. Чтобы убедиться, что он ловит ошибку, сломайте код намеренно:

text
Проверь силу тестов для apps/api/src/services/bookings.ts.
По очереди внеси 5 поломок: убери проверку автора в отмене,
поменяй '[)' на '[]' в диапазоне, сдвинь границу 21:00 на 22:00,
разреши бронь в прошлом, верни 200 вместо 409.
После каждой запускай тесты и откатывай поломку.
Дай таблицу: поломка → какой тест упал. Код не коммить.

Каждая строка таблицы без упавшего теста — дыра в сетке. Для регулярной проверки есть инструменты мутационного тестирования (например, StrykerJS для TypeScript), но и ручной вариант с агентом за десять минут даёт много.

Связь тестов со спецификацией

Попросите таблицу «правило → тесты» по всем R и S из спецификации:

text
Пройди по docs/SPEC.md и для каждого правила и сценария найди тесты,
которые его проверяют. Выведи таблицу. Правила без тестов — отдельным списком.

Пустые строки — ваш бэклог тестов. Таблицу полезно перегенерировать перед каждым релизом.

Ревью как второй слой

Тесты проверяют то, о чём вы подумали. Ревью ловит то, о чём не подумали. Команда /review в Codex запускает отдельного ревьюера, который выдаёт приоритизированные замечания и не меняет рабочее дерево — подробно в уроке «Ревью кода командой /review». Проверить незакоммиченные изменения из терминала можно и без интерактивной сессии:

bash
codex review --uncommitted

Полезно ревьюить в новой сессии, а не в той, где писался код: у автора в контексте все его оправдания.

Попробуйте сами

10–15 мин на рабочем месте
  1. Возьмите одно правило своей спецификации и пройдите красно-зелёный цикл с паузой после красного. Прочитайте тесты до реализации.
  2. Попросите агента внести пять намеренных поломок и составить таблицу «поломка → упавший тест».
  3. Запустите git diff --stat main -- '*.test.ts' на последней ветке и проверьте, менял ли агент старые тесты.

Коротко

  • Тесты позволяют не читать каждую строку, но сами тесты читать нужно.
  • Интеграционные тесты — с настоящей базой: часть правил живёт только в ней.
  • Сначала красный тест, пауза на чтение, потом реализация.
  • Агент умеет «чинить» тесты вместо кода — проверяйте дифф тестов.
  • Намеренные поломки показывают, ловит ли сетка ошибки.
  • /review в отдельной сессии — второй слой после тестов.
Отметка хранится только в вашем браузере
Хотите связаться с владельцем
компании напрямую?
Дмитрий Тарасов
Дмитрий Тарасов
СЕО

НАПИСАТЬ

Написать в MAX