TDD: red-green-refactor
Перевернём привычный порядок: сначала тест, потом код — и пройдём полный цикл на живом примере.
TDD (Test-Driven Development) — практика, при которой сначала пишут падающий тест, затем минимальный код, чтобы он прошёл, и только потом улучшают код: цикл red → green → refactor.
Три фазы цикла
- Red. Пишем тест на ещё не существующее поведение. Он падает — это нормально и ожидаемо.
- Green. Пишем самый простой код, чтобы тест стал зелёным. Красота пока не важна.
- Refactor. Тест зелёный — теперь улучшаем код, не меняя поведения. Тест страхует: если что-то сломаем — он покраснеет.
Зачем «сначала тест»
Это меняет мышление. Сначала формулируешь, что функция должна делать (в виде проверок), и только потом — как. Тесты получаются по определению, код — минимальным и проверяемым, а «потом напишу тесты» (которое никогда не наступает) исключено.
Живой пример: функция fizzbuzz
Задача: для числа вернуть «Fizz» если делится на 3, «Buzz» если на 5, «FizzBuzz» если на оба, иначе — само число строкой. Пройдём цикл целиком. Сначала формулируем тесты (red), затем реализуем (green) — здесь покажем уже зелёное состояние с готовой реализацией.
# GREEN: минимальная реализация, проходящая тесты
def fizzbuzz(n):
if n % 15 == 0:
return "FizzBuzz"
if n % 3 == 0:
return "Fizz"
if n % 5 == 0:
return "Buzz"
return str(n)
# Тесты, которые мы написали ПЕРВЫМИ (red), теперь зелёные
def test_fizzbuzz():
assert fizzbuzz(1) == "1"
assert fizzbuzz(3) == "Fizz"
assert fizzbuzz(5) == "Buzz"
assert fizzbuzz(15) == "FizzBuzz"
assert fizzbuzz(30) == "FizzBuzz"
test_fizzbuzz()
print("GREEN: все тесты fizzbuzz проходят")
Вывод:
GREEN: все тесты fizzbuzz проходят
Рефакторинг под защитой тестов
Теперь можно безопасно переписать функцию красивее — например, через сборку строки — и тот же набор тестов мгновенно подтвердит, что поведение не изменилось.
def fizzbuzz(n):
out = ""
if n % 3 == 0:
out += "Fizz"
if n % 5 == 0:
out += "Buzz"
return out or str(n)
# REFACTOR: реализация другая, тесты — те же и всё ещё зелёные
assert fizzbuzz(1) == "1"
assert fizzbuzz(3) == "Fizz"
assert fizzbuzz(5) == "Buzz"
assert fizzbuzz(15) == "FizzBuzz"
print("REFACTOR: поведение сохранено, тесты подтверждают")
Вывод:
REFACTOR: поведение сохранено, тесты подтверждают
Две совершенно разные реализации fizzbuzz прошли один и тот же набор тестов. Именно это даёт TDD: смелость менять код, потому что тесты ловят регрессии мгновенно.
Итог
- TDD: red (падающий тест) → green (минимальный код) → refactor (улучшение).
- Сначала формулируем «что», потом «как» — тесты получаются по определению.
- Тесты позволяют безопасно рефакторить: поведение зафиксировано.