Освойте шаблон запроса Claude Code, который генерирует высокосигнальные тесты, фокусируясь на границах, режимах отказа и инвариантах — вместо множества happy-path проверок.

Автогенерируемые наборы тестов часто выглядят впечатляюще: десятки тестов, много кода настройки, и каждое имя функции где‑то появляется. Но многие из этих тестов — это просто проверки «работает, когда всё нормально». Они легко проходят, редко ловят баги и всё равно требуют времени на чтение и поддержку.
При типичном запросе на генерацию тестов для Claude Code модель склонна зеркалить примерные входы, которые она видит. Вы получаете вариации, которые выглядят иначе, но покрывают одинаковое поведение. Результат — большой набор с тонким покрытием там, где это важно.
Высокосигнальные тесты другие. Это небольшой набор, который поймал бы инцидент прошлого месяца. Они падают, когда поведение меняется рискованно, и остаются стабильными при безвредных рефакторах. Один такой тест может быть ценнее двадцати проверок «возвращает ожидаемое значение».
Генерация множества happy-path тестов обычно даёт несколько явных симптомов:
Представьте функцию, которая применяет код скидки. Happy-path тесты подтверждают, что «SAVE10» снижает цену. Настоящие баги прячутся в другом месте: нулевые или отрицательные цены, просроченные коды, пограничные случаи округления или максимальные лимиты скидки. Именно эти случаи приводят к неверным итогам, недовольным клиентам и откатам в полночь.
Цель — перейти от «больше тестов» к «лучшим тестам», нацелившись на три направления: границы, режимы отказа и инварианты.
Если вы хотите высокосигнальные модульные тесты, перестаньте просить «ещё тестов» и начните просить три конкретных типа. Это суть запроса для Claude Code, который даёт полезное покрытие вместо груды «работает на нормальном вводе» проверок.
Границы — это края того, что код принимает или производит. Многие реальные дефекты — off-by-one, состояние пустоты или таймауты — не проявляются на happy path.
Думайте о минимумах и максимумах (0, 1, максимальная длина), пустом vs присутствующем ("", [], nil), off-by-one (n-1, n, n+1) и временных лимитах (рядом с порогом).
Пример: если API принимает «до 100 элементов», протестируйте 100 и 101, а не только 3.
Режимы отказа — это способы, которыми система может сломаться: плохие входы, отсутствующие зависимости, частичные результаты или ошибки вверх по стеку. Хорошие тесты режимов отказа проверяют поведение под нагрузкой, а не только вывод в идеальных условиях.
Пример: когда запрос к базе данных падает, возвращает ли функция понятную ошибку и избегает ли записи частичных данных?
Инварианты — это истины, которые должны оставаться верными до и после вызова. Они превращают расплывчатую корректность в чёткие утверждения.
Примеры:
Когда вы фокусируетесь на этих трёх целях, вы получаете меньше тестов, но каждый несёт больше сигнала.
Если вы просите тесты слишком рано, обычно получаете груду вежливых «работает как ожидается» проверок. Простое исправление — сначала написать маленький контракт, а затем генерировать тесты из этого контракта. Это самый быстрый способ превратить запрос Claude Code в нечто, что действительно находит баги.
Полезный контракт достаточно короткий, чтобы прочесть его в одно дыхание. Ориентируйтесь на 5–10 строк, отвечающих на три вопроса: что входит, что выходит и что ещё меняется.
Пишите контракт простым языком, а не кодом, и включайте только то, что можно протестировать.
Как только контракт готов, просканируйте его на предмет мест, где реальность может нарушить ваши допущения. Они становятся пограничными случаями (min/max, ноль, переполнение, пустые строки, дубликаты) и режимами отказа (тайм‑ауты, отказ доступа, нарушение уникальности, повреждённый ввод).
Вот конкретный пример для фичи вроде reserveInventory(itemId, qty):
Контракт может говорить, что qty должен быть положительным целым, функция должна быть атомарной и никогда не создавать отрицательный запас. Это сразу предлагает высокосигнальные тесты: qty = 0, qty = 1, qty больше доступного, конкурентные вызовы и принудительная ошибка базы данных на середине операции.
Если вы используете инструмент вроде Koder.ai, тот же рабочий процесс применим: сначала пишите контракт в чате, а затем генерируйте тесты, которые прямо атакуют границы, режимы отказа и список «не должно случаться».
Используйте этот запрос к Claude Code, когда хотите меньше тестов, но каждый из них должен нести вес. Ключевой ход — сначала потребовать план тестов, а затем генерировать код только после вашего одобрения плана.
You are helping me write HIGH-SIGNAL unit tests.
Context
- Language/framework: \u003cfill in\u003e
- Function/module under test: \u003cname + short description\u003e
- Inputs: \u003ctypes, ranges, constraints\u003e
- Outputs: \u003ctypes + meaning\u003e
- Side effects/external calls: \u003cdb, network, clock, randomness\u003e
Contract (keep it small)
1) Preconditions: \u003cwhat must be true\u003e
2) Postconditions: \u003cwhat must be true after\u003e
3) Error behavior: \u003chow failures are surfaced\u003e
Task
PHASE 1 (plan only, no code):
A) Propose 6-10 tests max. Do not include “happy path” unless it protects an invariant.
B) For each test, state: intent, setup, input, expected result, and WHY it is high-signal.
C) Invariants: list 3-5 invariants and how each will be asserted.
D) Boundary matrix: propose a small matrix of boundary values (min/max/empty/null/off-by-one/too-long/invalid enum).
E) Failure modes: list negative tests that prove safe behavior (no crash, no partial write, clear error).
Stop after PHASE 1 and ask for approval.
PHASE 2 (after approval):
Generate the actual test code with clear names and minimal mocks.
Практический трюк — требовать матрицу границ компактной таблицей, чтобы пробелы были очевидны:
| Dimension | Valid edge | Just outside | “Weird” value | Expected behavior |
|---|---|---|---|---|
| length | 0 | -1 | 10,000 | error vs clamp vs accept |
Если Claude предлагает 20 тестов, надавите на слияние похожих случаев и оставьте только те, которые действительно поймают баг (off-by-one, неверный тип ошибки, молчаливая потеря данных, сломанный инвариант).
Начните с маленького, конкретного контракта для поведения, которое хотите протестировать. Вставьте сигнатуру функции, короткое описание входов и выходов и существующие тесты (даже если это только happy-path). Это удерживает модель на том, что код действительно делает, а не на её догадках.
Далее потребуйте таблицу рисков перед тем, как просить код тестов. Потребуйте три колонки: пограничные случаи (границы допустимого ввода), режимы отказа (плохой ввод, отсутствие данных, таймауты) и инварианты (правила, которые всегда должны быть верны). Добавьте одну фразу на строку: “почему это может сломаться.” Простая таблица выявляет пробелы быстрее, чем гора файлов тестов.
Затем выберите минимальный набор тестов, где каждый имеет уникальную цель по нахождению бага. Если два теста падают по одной и той же причине, оставьте более сильный.
Практическое правило отбора:
Наконец, требуйте по короткому объяснению на тест: какой баг он бы поймал, если бы упал. Если объяснение расплывчато («валидирует поведение»), тест, скорее всего, низкоценностный.
Инвариант — правило, которое должно оставаться истинным для любого допустимого ввода. С инвариантно-ориентированным тестированием вы сначала пишете правило простым языком, затем превращаете его в утверждение, которое может громко упасть.
Выберите 1–2 инварианта, которые реально вас защищают от багов. Хорошие инварианты часто про безопасность (нет потери данных), согласованность (одинаковые входы → одинаковые выходы) или лимиты (никогда не превышать caps).
Запишите инвариант коротким предложением, затем решите, какие доказательства доступны в тесте: возвращаемые значения, сохранённые данные, эмитируемые события или вызовы зависимостей. Сильные утверждения проверяют и результат, и побочные эффекты, потому что многие баги скрываются в «вернул OK, но записал не то».
Например, у вас есть функция, применяющая купон к заказу:
Теперь закодируйте их как конкретные утверждения:
expect(result.total).toBeGreaterThanOrEqual(0)
expect(db.getOrder(orderId).discountCents).toBe(originalDiscountCents)
Избегайте расплывчатых утверждений типа «возвращает ожидаемый результат». Утверждайте конкретное правило (неотрицательность) и конкретный побочный эффект (скидка применена один раз).
Для каждого инварианта добавьте короткую заметку в тест о том, какие данные бы его нарушили. Это помогает тесту не превратиться позже в проверку happy-path.
Простой шаблон, который долго держится:
Высокосигнальные тесты часто подтверждают, что код корректно падает. Если модель пишет лишь happy-path тесты, вы узнаете мало о том, как фича ведёт себя при грязных входах и проблемах зависимостей.
Начните с определения, что значит «безопасно» для этой фичи. Возвращаете ли вы типизированную ошибку? Переходите к значению по умолчанию? Пытайтесь ли повторить один раз, а затем остановиться? Запишите это в одном предложении и заставьте тесты это доказывать.
Когда просите Claude Code генерацию тестов режимов отказа, делайте цель строгой: покрывайте способы, которыми система может сломаться, и утверждайте точный ожидаемый ответ. Полезная фраза: «Предпочитайте меньше тестов с сильными утверждениями, а не много поверхностных».
Категории отказов, которые дают лучшие тесты:
Пример: есть endpoint, который создаёт пользователя и вызывает почтовый сервис для приветственного письма. Низкоценностный тест проверит «возвращает 201». Высокосигнальный тест отказа проверит, что если сервис отправки почты тайм‑аутит, вы либо (a) всё ещё создаёте пользователя и возвращаете 201 с флагом "email_pending", либо (b) возвращаете понятный 503 и не создаёте пользователя. Выберите одно поведение и утверждайте и ответ, и побочные эффекты.
Также тестируйте, что вы не утекали данные. При ошибке валидации убедитесь, что в БД ничего не записано. При получении повреждённого payload от зависимости — убедитесь, что вы не бросаете непойманные исключения и не возвращаете сырые стектрейсы.
Низкоценностные наборы тестов обычно возникают, когда модель «вознаграждается» за объём. Если ваш запрос просит «20 unit тестов», вы часто получаете мелкие вариации, которые выглядят тщательными, но не ловят ничего нового.
Частые ловушки:
Пример: функция "create user": десять happy-path тестов могут варьировать email и всё равно пропустить важные вещи: отклонение дубликатов, пустой пароль и гарантию, что возвращаемые user ID уникальны и стабильны.
Правила-ограничители, полезные при ревью:
Представьте фичу: применение купона на кассе.
Контракт (маленький и тестируемый): принимая subtotal в центах и опциональный купон, вернуть итог в центах. Правила: процентные купоны округляют вниз до цента, фиксированные купоны вычитают фиксированную сумму, и итог никогда не меньше 0. Купон может быть недействительным, просроченным или уже использован.
Не просите «тесты для applyCoupon()». Просите тестирование пограничных случаев, режимов отказа и инвариантов, привязанных к этому контракту.
Выбирайте входы, которые ломают арифметику или валидацию: пустая строка купона, subtotal = 0, subtotal чуть ниже и выше минимума для скидки, фиксированная скидка больше subtotal и процент вроде 33%, который создаёт вопросы округления.
Предположим, lookup купона может упасть или состояние может быть неверным: сервис купонов недоступен, купон просрочен или уже выкуплен этим пользователем. Тест должен доказать ожидаемое поведение (купон отклоняется с понятной ошибкой, итог не меняется).
Минимальный высокосигнальный набор тестов (5 тестов) и что каждый ловит:
Если эти тесты проходят, вы покрыли типичные точки поломки без набивания набора тестов дубликатами happy-path.
Перед тем как принять результат модели, проведите быструю проверку качества. Цель — тесты, которые каждый защищают от конкретной, вероятной ошибки.
Используйте этот чеклист как ворота:
Практический приём после генерации: переименуйте тесты в формат «should <поведение> when <условие>» и «should not <плохой исход> when <отказ>». Если переименование не удаётся сделать чётко, тест слабо сфокусирован.
Если вы используете Koder.ai, этот цикл хорошо ложится на снимки и откат: сгенерировали тесты, запустили, при необходимости откатили, пока высокосигнальный набор не устоится.
Относитесь к вашему промпту как к повторяемому хаберу, а не как к одноразовому запросу. Сохраните один шаблон (тот, который заставляет проверять границы, режимы отказа и инварианты) и используйте его для каждой новой функции, endpoint-а или UI‑потока.
Простая привычка, которая быстро улучшает результаты: требуйте по одному предложению на тест, объясняющему, какой баг он поймает. Если это предложение общее, тест — шум.
Ведите живой список доменных инвариантов для вашего продукта. Не храните его в голове. Добавляйте туда каждый раз, когда находите реальную ошибку.
Лёгкий рабочий цикл, который можно повторять:
Если вы создаёте приложения через чат, запускайте этот цикл внутри Koder.ai (koder.ai) — так контракт, план и сгенерированные тесты останутся в одном месте. Когда рефактор неожиданно изменит поведение, снимки и откат помогут сравнить и довести набор высокосигнальных тестов до стабильности.
По умолчанию: стремитесь к небольшому набору тестов, которые действительно поймали бы реальную ошибку.
Практичный предел — 6–10 тестов на единицу (функция/модуль). Если нужно больше, обычно это признак того, что единица делает слишком многое или контракт неясен.
Тесты «по счастливому пути» в основном доказывают, что пример по-прежнему работает. Они часто упускают вещи, которые ломаются в продакшене.
Высокосигнальные тесты нацелены на:
Начните с краткого контракта, который можно прочесть в один вдох:
Затем генерируйте тесты из этого контракта, а не только из примеров.
Сначала протестируйте эти случаи:
Хороший тест на режим отказа доказывает два момента:
Если involved запись в БД — всегда проверьте, что произошло в хранилище после ошибки.
Стандартный подход: превратите инвариант в утверждение по наблюдаемым результатам.
Примеры:
expect(total).toBeGreaterThanOrEqual(0)Стоит оставить happy-path тест, когда он защищает инвариант или критическую интеграцию.
Хорошие причины оставить его:
В противном случае замените его на пограничные/отказные тесты, которые ловят больше классов ошибок.
На этапе PHASE 1 требуйте только план.
Попросите модель предоставить:
Только после утверждения плана — генерируйте код. Это предотвращает «20 похожих тестов».
Правило по умолчанию: мокайте только ту границу, которая вам не принадлежит (БД/сеть/часы), и держите всё остальное реальным.
Чтобы избежать лишнего мока:
Если тест «ломается при рефакторе, но поведение не поменялось», значит он слишком связан с реализацией или перетестирован.
Простой «тест удаления» поможет понять ценность теста:
Проверяйте дубликаты:
Выбирайте по одному-двум на измерение входа так, чтобы каждый тест покрывал уникальный риск.
Проверяйте и возврат значения, и побочные эффекты — многие баги скрываются в «вернул OK, но записал неправильно».