Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Поддержать проект
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. TDD: Когда работает, когда мешает
tdd_when_works_when_hinders

TDD: Когда работает, когда мешает

Честный разговор о TDD: где помогает, где замедляет. Прагматичный подход без фанатизма

TDD не серебряная пуля. Это инструмент, который работает в конкретных ситуациях. Честный разговор о том, когда использовать TDD, а когда нет.

#✅ Когда TDD РАБОТАЕТ отлично

#1. Чёткие бизнес-требования

# ✅ ОТЛИЧНО: Requirements понятны """ Requirement: Discount calculation - Regular customer: 0% discount - Gold customer (>10 orders): 10% discount - Gold customer on Friday: 15% discount """ # TDD идеален — requirements → tests → code def test_regular_customer_no_discount(): discount = calculate_discount("regular", orders=5, day="monday") assert discount == 0.0 def test_gold_customer_discount(): discount = calculate_discount("gold", orders=15, day="monday") assert discount == 0.10

Почему работает: Requirements детерминированы → тесты очевидны.

#2. Pure functions / утилиты

# ✅ ОТЛИЧНО: Pure function без side effects def test_format_phone_number(): assert format_phone("+14155552671") == "(415) 555-2671" assert format_phone("4155552671") == "(415) 555-2671" assert format_phone("415-555-2671") == "(415) 555-2671" # Реализация def format_phone(number: str) -> str: digits = ''.join(c for c in number if c.isdigit()) return f"({digits[0:3]}) {digits[3:6]}-{digits[6:10]}"

Почему работает: Нет зависимостей, детерминировано, легко тестировать.

#3. API contracts

# ✅ ОТЛИЧНО: API контракт должен быть стабильным def test_create_user_api_contract(): """TDD гарантирует контракт не сломается""" response = client.post("/users", json={"email": "test@test.com"}) assert response.status_code == 201 assert "id" in response.json() assert "email" in response.json() assert "created_at" in response.json()

Почему работает: Контракт критичен, TDD предотвращает breaking changes.

#4. Сложная логика с edge cases

# ✅ ОТЛИЧНО: Много edge cases @pytest.mark.parametrize("input,expected", [ ("", False), # Empty ("a", False), # Single char ("aa", True), # Simple palindrome ("A man a plan a canal Panama", True), # Complex with spaces ("race a car", False), # Not palindrome ]) def test_is_palindrome(input, expected): assert is_palindrome(input) == expected

Почему работает: Тесты документируют все edge cases.

#❌ Когда TDD МЕШАЕТ

#1. Exploratory coding

# ❌ ПЛОХО: Вы не знаете как работает библиотека import new_obscure_library def test_library_does_something(): result = new_obscure_library.process(data) assert result == ??? # Что проверять???

Решение: Сначала explore (spike), потом тесты.

# ✅ ХОРОШО: Spike first # 1. Experiment в REPL / Jupyter result = new_obscure_library.process({"key": "value"}) print(result) # {'status': 'ok', 'processed': True} # 2. Теперь тесты понятны def test_library_process(): result = new_obscure_library.process({"key": "value"}) assert result["status"] == "ok"

#2. UI / визуальный код

# ❌ ПЛОХО: TDD для UI бессмысленно def test_button_looks_good(): button = render_button(color="blue", size="large") assert button.color == "#0000FF" # Субъективно! assert button.width == 200 # А если дизайн изменится?

Решение: Visual regression tests (screenshot comparison) или manual testing.

#3. Прототипирование / MVP

# ❌ ПЛОХО: Тесты замедляют iteration # Вы не знаете какой API нужен class UserService: def create_user(self, email): # Или user_data dict? pass # Или validate_and_create? Или register? # Тесты будут переписаны 10 раз за день

Решение: Сначала prototype API без тестов, затем стабилизируйте с тестами.

#4. Throwaway scripts

# ❌ ПЛОХО: Тесты для одноразового кода def migrate_users_from_old_db(): """Скрипт запустится один раз и больше никогда""" # 100 строк миграционной логики # Тесты бессмысленны — скрипт одноразовый

Решение: Запустите, проверьте вручную, удалите.

#Прагматичный подход: гибридная стратегия

#Стратегия 1: Outside-in TDD

# 1. Начните с integration test (outside) def test_create_todo_via_api(): response = client.post("/todos", json={"title": "Buy milk"}) assert response.status_code == 201 # 2. Затем unit tests (inside) def test_todo_service_create(): service = TodoService(mock_repo) todo = service.create("Buy milk") assert todo.title == "Buy milk"

Преимущества:

  • Integration test гарантирует end-to-end
  • Unit tests быстрые и детальные

#Стратегия 2: Test-after для exploration

# 1. Exploratory spike (без тестов) def process_payment(amount, card): # Эксперименты с Stripe API charge = stripe.Charge.create(amount=amount, source=card) return charge # 2. После понимания → TDD refactoring def test_process_payment_success(): mock_stripe = Mock() mock_stripe.Charge.create.return_value = Mock(id="ch_123") result = process_payment_v2(100, "tok_visa", stripe_client=mock_stripe) assert result["charge_id"] == "ch_123"

#Стратегия 3: Critical paths first

# ✅ TDD для critical business logic def test_calculate_invoice_total(): invoice = Invoice(items=[...], tax_rate=0.1, discount=0.05) assert invoice.total() == 105.0 # Критично для бизнеса! # ❌ NO TDD для вспомогательных утилит def format_invoice_date(date): return date.strftime("%Y-%m-%d") # Некритично, тест после

#Реальный пример: соотношение TDD

# Проект Todo App (100% code) # 40% — TDD (написано test-first) # - Business logic (subscriptions, payments) # - API contracts (REST endpoints) # - Critical utilities (discount calculation) # 30% — Test-after (написано code-first) # - Database models (знали структуру заранее) # - Simple CRUD operations # - UI components (после sketch) # 20% — No tests # - One-time migration scripts # - Debug utilities # - Local development tools # 10% — Manual testing # - UI appearance # - Email templates # - Admin dashboard

#Метрики для решения: TDD или нет?

# Используйте TDD если: complexity = "high" # Много условий / edge cases requirements = "clear" # Знаете что делать change_frequency = "high" # Часто меняется criticality = "high" # Баги дорого обходятся # НЕ используйте TDD если: complexity = "low" # Простая логика requirements = "unclear" # Не знаете что нужно change_frequency = "low" # Редко меняется prototype = True # Эксперимент / spike

#Заключение: прагматизм

# ❌ Фанатизм "100% TDD всегда и везде!" # Медленно и неэффективно # ✅ Прагматизм """ TDD для: - Critical business logic - Public APIs - Complex algorithms Code-first для: - Exploration / spikes - Simple CRUD - UI prototypes No tests для: - One-time scripts - Debug tools """

Итог: TDD — мощный инструмент, но не догма. Используйте там, где он даёт ценность.