Главная · Скиллы · test-driven-development

test-driven-developmentстрогий TDD

obra/superpowers

Enforces строгую разработку через тесты: тесты обязательно до кода реализации. Не позволяет писать production код без покрывающих тестов.

Установка

npx -y skills add obra/superpowers --skill test-driven-development --agent claude-code

Разработка через тесты (TDD)

Обзор

Сначала напишите тест. Посмотрите, как он падает. Напишите минимальный код, чтобы он прошёл.

Главный принцип: если вы не видели, как тест падает, вы не знаете, тестирует ли он нужное.

Нарушение буквы правил — это нарушение их духа.

Когда использовать

Всегда:

  • Новые функции
  • Исправления багов
  • Рефакторинг
  • Изменения поведения

Исключения (спросите партнёра-человека):

  • Одноразовые прототипы
  • Сгенерированный код
  • Файлы конфигурации

Думаете «пропущу TDD только в этот раз»? Стоп. Это самооправдание.

Железный закон

НИКАКОГО ПРОДАКШЕН-КОДА БЕЗ СНАЧАЛА ПАДАЮЩЕГО ТЕСТА

Написали код до теста? Удалите его. Начните заново.

Без исключений:

  • Не оставляйте «для справки»
  • Не «адаптируйте» его при написании тестов
  • Не смотрите на него
  • Удалить значит удалить

Реализуйте заново из тестов. Точка.

Red-Green-Refactor

digraph tdd_cycle {
    rankdir=LR;
    red [label="RED\nWrite failing test", shape=box, style=filled, fillcolor="#ffcccc"];
    verify_red [label="Verify fails\ncorrectly", shape=diamond];
    green [label="GREEN\nMinimal code", shape=box, style=filled, fillcolor="#ccffcc"];
    verify_green [label="Verify passes\nAll green", shape=diamond];
    refactor [label="REFACTOR\nClean up", shape=box, style=filled, fillcolor="#ccccff"];
    next [label="Next", shape=ellipse];
    red -> verify_red;
    verify_red -> green [label="yes"];
    verify_red -> red [label="wrong\nfailure"];
    green -> verify_green;
    verify_green -> refactor [label="yes"];
    verify_green -> green [label="no"];
    refactor -> verify_green [label="stay\ngreen"];
    verify_green -> next;
    next -> red;
}

RED — напишите падающий тест

Напишите один минимальный тест, показывающий, что должно произойти.

Хорошо (ясное имя, тестирует реальное поведение, одно дело):

test('retries failed operations 3 times', async () => {
  let attempts = 0;
  const operation = () => {
    attempts++;
    if (attempts < 3) throw new Error('fail');
    return 'success';
  };
  const result = await retryOperation(operation);
  expect(result).toBe('success');
  expect(attempts).toBe(3);
});

Плохо (расплывчатое имя, тестирует мок, а не код):

test('retry works', async () => {
  const mock = jest.fn()
    .mockRejectedValueOnce(new Error())
    .mockRejectedValueOnce(new Error())
    .mockResolvedValueOnce('success');
  await retryOperation(mock);
  expect(mock).toHaveBeenCalledTimes(3);
});

Требования: одно поведение; ясное имя; реальный код (без моков, если их можно избежать).

Проверьте RED — посмотрите, как падает

ОБЯЗАТЕЛЬНО. Никогда не пропускайте.

npm test path/to/test.test.ts

Убедитесь: тест падает (а не ошибается); сообщение о провале ожидаемое; падает из-за отсутствия функции (а не из-за опечаток).

Тест проходит? Вы тестируете существующее поведение. Поправьте тест. Тест ошибается? Исправьте ошибку, перезапускайте, пока не упадёт правильно.

GREEN — минимальный код

Напишите простейший код, чтобы тест прошёл.

Хорошо (ровно столько, чтобы пройти):

async function retryOperation<T>(fn: () => Promise<T>): Promise<T> {
  for (let i = 0; i < 3; i++) {
    try { return await fn(); }
    catch (e) { if (i === 2) throw e; }
  }
  throw new Error('unreachable');
}

Плохо (переусложнено, YAGNI):

async function retryOperation<T>(
  fn: () => Promise<T>,
  options?: { maxRetries?: number; backoff?: 'linear' | 'exponential'; onRetry?: (attempt: number) => void; }
): Promise<T> { /* YAGNI */ }

Не добавляйте функции, не рефакторьте чужой код и не «улучшайте» сверх теста.

Проверьте GREEN — посмотрите, как проходит

ОБЯЗАТЕЛЬНО. Убедитесь: тест проходит; другие тесты по-прежнему проходят; вывод чистый (без ошибок и предупреждений). Тест падает? Чините код, а не тест. Другие тесты падают? Чините сейчас.

REFACTOR — наведите чистоту

Только после зелёного: уберите дублирование; улучшите имена; вынесите хелперы. Держите тесты зелёными. Не добавляйте поведение.

Повторите

Следующий падающий тест для следующей функции.

Хорошие тесты

КачествоХорошоПлохо
МинимальныйОдно дело. «and» в имени? Разделите.test('validates email and domain and whitespace')
ЯсныйИмя описывает поведениеtest('test1')
Показывает намерениеДемонстрирует желаемый APIСкрывает, что код должен делать

Почему порядок важен

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

«Я уже вручную протестировал все краевые случаи» — ручное тестирование стихийно. Нет записи о проверенном; нельзя перезапустить при изменении кода; легко забыть случаи под давлением. Автотесты систематичны.

«Удалять X часов работы расточительно» — ошибка невозвратных затрат. Время уже потрачено. Выбор сейчас: удалить и переписать с TDD (ещё X часов, высокая уверенность) или оставить и добавить тесты потом (30 минут, низкая уверенность, вероятные баги). «Расточительство» — это держать код, которому нельзя доверять.

«TDD догматичен, прагматизм — это адаптация» — TDD И ЕСТЬ прагматизм: находит баги до коммита; предотвращает регрессии; документирует поведение; включает рефакторинг. «Прагматичные» срезки = отладка в продакшене = медленнее.

«Тесты-после достигают тех же целей» — нет. Тесты-после отвечают «что это делает?», тесты-сначала — «что это должно делать?». Тесты-после смещены вашей реализацией. 30 минут тестов после ≠ TDD.

Частые самооправдания

ОтговоркаРеальность
«Слишком просто, чтобы тестировать»Простой код ломается. Тест — 30 секунд.
«Протестирую после»Тесты, проходящие сразу, ничего не доказывают.
«Тесты-после достигают тех же целей»После = «что это делает?». Сначала = «что должно делать?».
«Уже протестировал вручную»Стихийно ≠ систематично. Нет записи, нельзя перезапустить.
«Удалять X часов расточительно»Невозвратные затраты. Непроверенный код — техдолг.
«Оставлю для справки, тесты — сначала»Вы его адаптируете. Это тесты-после. Удалить значит удалить.
«Нужно сначала исследовать»Ок. Выбросьте исследование, начните с TDD.
«Тяжело тестировать = дизайн неясен»Слушайте тест. Тяжело тестировать = тяжело использовать.
«TDD меня замедлит»TDD быстрее отладки. Прагматично = тест-сначала.

Красные флаги — СТОП и начните заново

  • Код до теста
  • Тест после реализации
  • Тест проходит сразу
  • Не можете объяснить, почему тест упал
  • Тесты добавлены «позже»
  • Самооправдание «только в этот раз»
  • «Я уже протестировал вручную»
  • «Оставлю для справки» / «адаптирую существующий код»
  • «Уже потратил X часов, удалять расточительно»
  • «Это другое, потому что…»

Всё это означает: удалить код. Начать заново с TDD.

Пример: исправление бага

Баг: пустой email принимается.

RED

test('rejects empty email', async () => {
  const result = await submitForm({ email: '' });
  expect(result.error).toBe('Email required');
});

Проверка RED

$ npm test
FAIL: expected 'Email required', got undefined

GREEN

function submitForm(data: FormData) {
  if (!data.email?.trim()) { return { error: 'Email required' }; }
  // ...
}

Проверка GREEN

$ npm test
PASS

REFACTOR — при необходимости вынесите валидацию для нескольких полей.

Чек-лист проверки

Прежде чем отмечать работу завершённой:

  • [ ] У каждой новой функции/метода есть тест
  • [ ] Видели падение каждого теста до реализации
  • [ ] Каждый тест упал по ожидаемой причине (нет функции, а не опечатка)
  • [ ] Написали минимальный код для прохождения каждого теста
  • [ ] Все тесты проходят
  • [ ] Вывод чистый (без ошибок и предупреждений)
  • [ ] Тесты используют реальный код (моки только если неизбежно)
  • [ ] Краевые случаи и ошибки покрыты

Не можете отметить все пункты? Вы пропустили TDD. Начните заново.

Когда застряли

ПроблемаРешение
Не знаю, как тестироватьНапишите желаемый API. Сначала утверждение. Спросите партнёра-человека.
Тест слишком сложныйДизайн слишком сложный. Упростите интерфейс.
Нужно мокать всёКод слишком связан. Используйте dependency injection.
Огромная настройка тестаВынесите хелперы. Всё ещё сложно? Упростите дизайн.

Интеграция с отладкой

Нашли баг? Напишите падающий тест, воспроизводящий его. Следуйте циклу TDD. Тест доказывает исправление и предотвращает регрессию. Никогда не чините баги без теста.

Антипаттерны тестирования

Добавляя моки или тестовые утилиты, прочтите testing-anti-patterns.md, чтобы избежать частых ловушек: тестирование поведения мока вместо реального; добавление методов «только для тестов» в продакшен-классы; мокирование без понимания зависимостей.

Финальное правило

Production code → test exists and failed first
Otherwise → not TDD

Никаких исключений без разрешения вашего партнёра-человека.

Из того же репозитория

brainstormingобязательное планирование перед кодингом
obra/superpowers
Блокирует реализацию до завершения планирования: контекст, уточняющие вопросы, варианты подхода, согласование дизайна. Генерирует spec-документы с автопроверкой на противоречия.
215.9k197.2k установок
using-superpowersмета-скилл для маркетплейса Claude
obra/superpowers
Мета-скилл, который заставляет Claude реально вызывать скиллы перед тем как что-то делать самостоятельно. Основа работы всего маркетплейса Claude Code.
215.9k125.3k установок
systematic-debuggingсистемный подход к отладке
obra/superpowers
Четырёхфазный протокол отладки до любых правок: сбор доказательств, постановка гипотезы, изоляция проблемы, исправление. Запрещает гадать без данных.
215.9k124.6k установок
writing-plansпланы реализации с кодом
obra/superpowers
Преобразует спецификации в пошаговые планы реализации с фрагментами кода, точными путями к файлам и конкретными командами тестов.
215.9k123.9k установок
requesting-code-reviewнезависимое код-ревью подагентом
obra/superpowers
Запускает подагент-ревьюера с чистым контекстом только об изменениях — без истории сессии. Чистые, непредвзятые отзывы без накопленных предположений.
215.9k111.1k установок
executing-plansпошаговое выполнение планов
obra/superpowers
Загружает план реализации, критически проверяет на пробелы, затем поэтапно выполняет с чекпоинтами. Останавливается при неясностях вместо движения вперёд наугад.
215.9k101.3k установок