Инженерия ПО

TDD и BDD

Кент Бек, создатель TDD, рассказывал как однажды его попросили реализовать многовалютную финансовую систему. Вместо того чтобы проектировать архитектуру, он написал один тест: `expect(Money.dollar(5).plus(Money.franc(10))).equals(Money.dollar(10))`. Тест не компилировался. Но в нём была вся суть системы. Через 2 часа работы красно-зеленого цикла архитектура сформировалась сама - и она оказалась проще, чем любой вариант, который мог бы получиться при классическом проектировании.

  • **Google:** внутренние исследования показали, что проекты с высокой тестовой культурой (включая TDD-практики) имеют значительно более низкий показатель production incidents
  • **Pivotal/VMware:** весь продуктовый код пишется в паре и через TDD; это ключевая инженерная практика компании, не опциональная
  • **NHS Digital (Великобритания):** перевод критических медицинских систем на BDD с Gherkin позволил клиническим специалистам проверять спецификации без технического посредника

Red-Green-Refactor цикл

TDD - это не про тестирование. Это про проектирование. Написание теста до кода заставляет думать об API, интерфейсах и контрактах прежде чем думать о реализации. Разработчик, пишущий тест первым, автоматически становится первым потребителем своего API - и сразу видит, удобно ли его использовать. Красный тест - это спецификация. Зеленый тест - это минимальная реализация. Рефакторинг - это чистота.

Три закона TDD (Robert C. Martin): 1) Нельзя писать production-код, пока нет падающего теста. 2) Нельзя писать больше теста, чем нужно для падения. 3) Нельзя писать больше production-кода, чем нужно для прохождения теста. Цикл: минуты, не часы. Не нужно планировать идеальную архитектуру заранее - она возникает через рефакторинг.

В TDD, зачем нужно убедиться, что тест сначала ПАДАЕТ (red) перед написанием реализации?

Given-When-Then и BDD

BDD - это TDD с другим словарем. Если TDD говорит 'красный-зеленый-рефакторинг', BDD говорит 'дано-когда-тогда'. Разница не техническая - она коммуникационная. Given-When-Then описывает поведение на языке, понятном не только разработчику, но и продуктовому менеджеру, QA и бизнесу. Спецификация становится исполняемой документацией.

Given-When-Then (GWT): Given - начальный контекст (preconditions), When - действие или событие, Then - ожидаемый результат. Gherkin - DSL для записи GWT сценариев в человекочитаемом виде. Cucumber (Java/Ruby), SpecFlow (.NET), Behave (Python) - фреймворки, выполняющие Gherkin-файлы. Jest/Vitest используют describe/it для неформального GWT.

В чём основная цель BDD по сравнению с обычным unit-тестированием?

Test-First проектирование

Test-first меняет направление силы: вместо 'написал код - добавил тест' появляется 'написал тест - код вырос из него'. Главное следствие - testable-by-design архитектура. Код, написанный test-first, как правило имеет меньше связей, четче разграниченные ответственности и более явные зависимости - потому что иначе его невозможно было бы протестировать изолированно.

Признаки тяжело тестируемого кода: скрытые зависимости (new Database() внутри функции вместо инъекции), глобальное состояние, смешивание бизнес-логики и I/O. Test-first обнаруживает эти проблемы немедленно - тест потребует инъекцию зависимостей. Это один из аргументов в пользу TDD: плохая архитектура создает боль при написании первого теста.

Разработчик пишет тест для нового класса и обнаруживает, что для создания объекта нужно передать 8 параметров. Что это сигнализирует с точки зрения TDD?

BDD инструменты: Vitest и Cucumber

Vitest и Jest предоставляют BDD-подобный синтаксис через describe/it прямо из коробки - это достаточно для большинства проектов. Cucumber с Gherkin-файлами нужен тогда, когда нетехнические участники команды (продакты, аналитики) должны писать или читать спецификации. Это двусторонний контракт: Cucumber требует дисциплины поддержания step definitions актуальными.

Vitest - современная альтернатива Jest для Vite-проектов: совместимый API, поддержка ESM, встроенная поддержка TypeScript, workspace для монорепо. Vitest UI - браузерный интерфейс для просмотра тестов. @cucumber/cucumber для Node.js: Gherkin сценарии -> step definitions -> assertions. Living documentation через Cucumber HTML Reports.

TDD замедляет разработку, потому что нужно писать вдвое больше кода

TDD замедляет начало, но ускоряет итерации: меньше дебага, более безопасный рефакторинг, меньше регрессий

Исследования (Microsoft Research, Google) показывают: TDD увеличивает время написания на 15-35%, но снижает количество дефектов на 40-90%. Экономия на дебаге и hotfix'ах с лихвой компенсирует начальные инвестиции уже на масштабе в несколько итераций.

Когда стоит использовать Cucumber с Gherkin вместо просто Jest/Vitest с describe/it?

Ключевые идеи

  • **Red-Green-Refactor** - не про тесты, а про проектирование; тест до кода делает API удобным для потребителя
  • **Given-When-Then** - универсальный язык между бизнесом и разработкой; описывает поведение, а не реализацию
  • **Test-first** выявляет архитектурные проблемы немедленно: тяжело тестируемый код - признак плохой архитектуры
  • **Cucumber vs Jest/Vitest** - выбирать по реальной потребности; Gherkin нужен только если нетехнические участники читают спецификации

Связанные темы

TDD и BDD определяют ритм всей инженерной практики:

  • Integration и E2E Testing — TDD применяется на уровне unit; BDD-сценарии часто реализуются как E2E тесты
  • Рефакторинг и чистый код — Тестовое покрытие - главный enabler безопасного рефакторинга

Вопросы для размышления

  • Если TDD улучшает дизайн кода, почему многие опытные разработчики избегают его на практике? Что является реальным барьером?
  • Как применять TDD к системам с высокой долей I/O (HTTP-запросы, файловая система, очереди сообщений)? Где граница между мокированием и реальным тестированием?
  • BDD-спецификации на Gherkin могут устаревать, если нет дисциплины их поддержания. Как организовать процесс, чтобы living documentation оставалась живой?

Связанные уроки

  • stat-05-hypothesis
TDD и BDD

0

1

Войти