Перейти к содержанию
Innovialabs

Разработка через тестирование (TDD): пошаговое руководство

Практика3 мин

Коротко

TDD — это короткий цикл: сначала пишете падающий тест, затем минимальный код, чтобы он прошёл, и после этого улучшаете код под защитой тестов.

Что такое TDD в двух словах

Test-Driven Development — подход, при котором тест пишется раньше кода. Работа идёт короткими циклами из трёх шагов:

  1. Red — пишете тест на маленькое поведение, запускаете и видите, что он падает.
  2. Green — пишете минимальный код, чтобы тест прошёл. Не красиво, а просто.
  3. Refactor — улучшаете структуру кода и тестов, не меняя поведения. Тесты должны остаться зелёными.

Один цикл занимает минуты. Смысл не в «покрытии», а в том, что каждый кусок кода появляется в ответ на конкретное требование.

Пример: стоимость корзины с промокодом

Задача: посчитать итог корзины, промокод SALE10 даёт скидку 10%. Используем Python и pytest.

Цикл 1, red. Начинаем с самого простого случая:

# test_cart.py
from cart import cart_total

def test_empty_cart_costs_zero():
    assert cart_total([]) == 0

Тест падает: модуля cart ещё нет. Это правильный красный — падение по ожидаемой причине.

Green. Минимальный код:

# cart.py
def cart_total(items):
    return 0

Да, это «жульничество». Но следующий тест заставит написать настоящую логику.

Цикл 2. Добавляем тест на сумму позиций:

def test_sums_price_times_quantity():
    items = [{"price": 100, "qty": 2}, {"price": 50, "qty": 1}]
    assert cart_total(items) == 250

Красный. Делаем зелёным:

def cart_total(items):
    return sum(i["price"] * i["qty"] for i in items)

Цикл 3. Промокод:

def test_sale10_gives_ten_percent_off():
    items = [{"price": 200, "qty": 1}]
    assert cart_total(items, promo="SALE10") == 180

def test_unknown_promo_is_ignored():
    items = [{"price": 200, "qty": 1}]
    assert cart_total(items, promo="WRONG") == 200

Green и refactor. После того как тесты прошли, выносим скидки в словарь, чтобы новые промокоды добавлялись одной строкой:

DISCOUNTS = {"SALE10": 0.10}

def cart_total(items, promo=None):
    subtotal = sum(i["price"] * i["qty"] for i in items)
    return subtotal * (1 - DISCOUNTS.get(promo, 0))

Все четыре теста зелёные — рефакторинг безопасен. Для денег в реальном проекте стоит использовать Decimal или целые копейки, а не float; это тоже удобно зафиксировать отдельным тестом.

Где TDD окупается

  • Бизнес-логика с понятными правилами: расчёты, скидки, тарифы, валидация, статусы заказов.
  • Исправление багов: сначала тест, который воспроизводит ошибку, потом фикс. Баг больше не вернётся незаметно.
  • Код, который часто меняется: тесты дают смелость рефакторить.
  • Публичные API и библиотеки: тест заставляет сначала подумать об удобстве интерфейса.

Где TDD мешает

  • Прототипы и эксперименты, которые завтра выбросят. Сначала выясните, что строить.
  • Вёрстка и визуальная часть: результат проверяется глазами, а не assert.
  • Тонкие обёртки над внешними сервисами: там полезнее интеграционные тесты.
  • Незнакомая технология: сначала короткий spike без тестов, потом TDD на чистовой версии.

TDD — инструмент, а не религия. Многие команды применяют его для ядра логики и пишут тесты «после» для остального.

Частые ошибки новичков

  • Слишком большой шаг. Тест сразу на всю фичу — и вы полчаса в красном. Дробите до одного поведения.
  • Пропуск красной фазы. Если не видели падения, тест может проверять не то или не запускаться вовсе.
  • Пропуск рефакторинга. Без третьего шага TDD быстро даёт работающий, но запутанный код.
  • Тесты на реализацию. Проверяйте результат и поведение, а не то, какие приватные методы вызваны.
  • Лишние моки. Если для теста нужно подменить пять зависимостей, это сигнал о проблеме в дизайне кода.

Как начать в команде

  1. Выберите один модуль с чёткими правилами.
  2. Договоритесь, что каждый баг сначала закрывается тестом.
  3. Подключите запуск тестов в CI, чтобы красная сборка блокировала слияние.
  4. Через пару недель обсудите, где подход помог, а где был лишним.

Частые вопросы

На старте — да, нужно время на привычку. Дальше обычно экономится время на ручной проверке и поиске регрессий, но выигрыш зависит от типа кода.

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.