3. TDD: разработка через тесты

Что такое TDD?
Заголовок раздела «Что такое TDD?»TDD (Test-Driven Development) — методология, где тест пишется до реализации.
Цикл Red → Green → Refactor:
🔴 RED → Написать тест, который падает🟢 GREEN → Написать минимальный код, чтобы тест прошёл🔵 REFACTOR → Улучшить код, не меняя поведениеПример: функция slugify
Заголовок раздела «Пример: функция slugify»Шаг 1: 🔴 Написать тест (он падает)
Заголовок раздела «Шаг 1: 🔴 Написать тест (он падает)»import { slugify } from './slugify';
describe('slugify', () => { test('converts spaces to hyphens', () => { expect(slugify('Hello World')).toBe('hello-world'); });});// ❌ FAIL: Cannot find module './slugify'Шаг 2: 🟢 Минимальная реализация
Заголовок раздела «Шаг 2: 🟢 Минимальная реализация»export function slugify(str) { return str.toLowerCase().replace(/\s+/g, '-');}// ✅ PASSШаг 3: Добавить тесты → 🔴 → 🟢
Заголовок раздела «Шаг 3: Добавить тесты → 🔴 → 🟢»describe('slugify', () => { test('converts spaces to hyphens', () => { expect(slugify('Hello World')).toBe('hello-world'); });
test('removes special characters', () => { expect(slugify('Hello, World!')).toBe('hello-world'); });
test('handles multiple spaces', () => { expect(slugify('too many spaces')).toBe('too-many-spaces'); });
test('handles Russian text', () => { expect(slugify('Привет мир')).toBe('privet-mir'); });
test('trims hyphens from edges', () => { expect(slugify(' Hello ')).toBe('hello'); });});// Улучшенная реализацияimport { transliterate } from 'transliteration';
export function slugify(str) { return transliterate(str) .toLowerCase() .replace(/[^a-z0-9\s-]/g, '') .trim() .replace(/\s+/g, '-') .replace(/-+/g, '-');}Пример TDD: класс ShoppingCart
Заголовок раздела «Пример TDD: класс ShoppingCart»describe('ShoppingCart', () => { let cart; beforeEach(() => { cart = new ShoppingCart(); });
// Тест 1 test('starts empty', () => { expect(cart.items).toEqual([]); expect(cart.total).toBe(0); });
// Тест 2 test('adds item', () => { cart.addItem({ id: 1, name: 'Apple', price: 1.5, qty: 2 }); expect(cart.items).toHaveLength(1); expect(cart.total).toBe(3.0); });
// Тест 3 test('increases qty for existing item', () => { cart.addItem({ id: 1, name: 'Apple', price: 1.5, qty: 1 }); cart.addItem({ id: 1, name: 'Apple', price: 1.5, qty: 2 }); expect(cart.items).toHaveLength(1); expect(cart.items[0].qty).toBe(3); });
// Тест 4 test('removes item', () => { cart.addItem({ id: 1, name: 'Apple', price: 1.5, qty: 1 }); cart.removeItem(1); expect(cart.items).toHaveLength(0); });
// Тест 5 test('applies discount', () => { cart.addItem({ id: 1, name: 'Apple', price: 100, qty: 1 }); cart.applyDiscount(10); // 10% expect(cart.total).toBe(90); });});// cart.js — реализация по мере добавления тестовexport class ShoppingCart { constructor() { this.items = []; this._discount = 0; }
get total() { const subtotal = this.items.reduce((sum, item) => sum + item.price * item.qty, 0); return subtotal * (1 - this._discount / 100); }
addItem(item) { const existing = this.items.find(i => i.id === item.id); if (existing) { existing.qty += item.qty; } else { this.items.push({ ...item }); } }
removeItem(id) { this.items = this.items.filter(i => i.id !== id); }
applyDiscount(percent) { this._discount = percent; }}Преимущества и недостатки TDD
Заголовок раздела «Преимущества и недостатки TDD»Плюсы:
- Код покрыт тестами с первого дня
- Продумываешь API до написания кода
- Меньше отладки, больше уверенности
- Тесты — живая документация
Минусы:
- Поначалу медленнее
- Требует дисциплины
- Сложнее для UI и интеграций
- Не всегда уместен (прототипы, R&D)
Когда использовать TDD?
Заголовок раздела «Когда использовать TDD?»✅ Бизнес-логика (корзина, расчёты, валидация)
✅ Алгоритмы и трансформации данных
✅ Публичное API библиотек
❌ Прототипы и эксперименты
❌ Простые CRUD без логики
❌ Настройка инфраструктуры
Практические задания
Заголовок раздела «Практические задания»- Реализуй функцию
validatePasswordчерез TDD (длина, спецсимволы, цифры) - Создай класс
EventEmitterчерез TDD (on, off, emit) - Применить TDD к функции
paginate(items, page, perPage)
- TDD: тест → реализация → рефакторинг
- Красный → Зелёный → Рефакторинг
- Хорошо подходит для бизнес-логики
- Тест описывает желаемое поведение до кода