Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

Test-Driven Development (TDD)

TDD to metodologia tworzenia oprogramowania, w której najpierw piszesz testy, a dopiero potem kod produkcyjny. W Parku Jurajskim to jak najpierw zaprojektowanie wszystkich systemów bezpieczeństwa, a dopiero potem sprowadzenie dinozaurów.

Cykl Red-Green-Refactor

TDD opiera się na trzech powtarzających się krokach:

1. RED - Napisz test, który nie przechodzi

1// KROK 1: Piszemy test PRZED kodem produkcyjnym
2describe('FenceSystem', () => {
3  it('should create a fence with voltage', () => {
4    const fence = createFence('Zone A', 10000);
5
6    expect(fence.zone).toBe('Zone A');
7    expect(fence.voltage).toBe(10000);
8    expect(fence.isActive).toBe(true);
9  });
10});
11// Wynik: RED - createFence nie istnieje!

2. GREEN - Napisz minimum kodu, żeby test przeszedł

1// KROK 2: Piszemy najprostszy kod, który przejdzie test
2function createFence(zone, voltage) {
3  return {
4    zone,
5    voltage,
6    isActive: true
7  };
8}
9// Wynik: GREEN - test przechodzi!

3. REFACTOR - Popraw kod zachowując zielone testy

1// KROK 3: Ulepszamy kod (testy nadal zielone!)
2class Fence {
3  constructor(zone, voltage) {
4    this.zone = zone;
5    this.voltage = voltage;
6    this.isActive = true;
7  }
8
9  getStatus() {
10    return this.isActive ? 'ACTIVE' : 'OFFLINE';
11  }
12}
13
14function createFence(zone, voltage) {
15  return new Fence(zone, voltage);
16}
17// Wynik: GREEN - testy nadal przechodzą po refaktorze!

Praktyczny przykład TDD

Zbudujmy system karmienia dinozaurów krok po kroku:

Iteracja 1: Podstawowe karmienie

1// RED: Piszemy test
2test('carnivore eats meat', () => {
3  const feeder = new DinoFeeder();
4  const result = feeder.feed('T-Rex', 'meat');
5  expect(result.success).toBe(true);
6});
7
8// GREEN: Minimalna implementacja
9class DinoFeeder {
10  feed(species, food) {
11    return { success: true };
12  }
13}
14// Test przechodzi - ale implementacja jest zbyt prosta!

Iteracja 2: Walidacja diety

1// RED: Dodajemy test dla niepoprawnego jedzenia
2test('carnivore rejects plants', () => {
3  const feeder = new DinoFeeder();
4  const result = feeder.feed('T-Rex', 'plants');
5  expect(result.success).toBe(false);
6  expect(result.reason).toBe('Wrong diet');
7});
8
9// GREEN: Rozszerzamy implementację
10class DinoFeeder {
11  constructor() {
12    this.diets = {
13      'T-Rex': 'carnivore',
14      'Triceratops': 'herbivore',
15      'Velociraptor': 'carnivore',
16    };
17  }
18
19  feed(species, food) {
20    const diet = this.diets[species];
21
22    if (diet === 'carnivore' && food !== 'meat') {
23      return { success: false, reason: 'Wrong diet' };
24    }
25    if (diet === 'herbivore' && food !== 'plants') {
26      return { success: false, reason: 'Wrong diet' };
27    }
28
29    return { success: true };
30  }
31}

Iteracja 3: Nieznany gatunek

1// RED: Test dla nieznanego gatunku
2test('unknown species throws error', () => {
3  const feeder = new DinoFeeder();
4  expect(() => feeder.feed('Unknown', 'meat')).toThrow('Unknown species');
5});
6
7// GREEN: Dodajemy obsługę błędów
8class DinoFeeder {
9  // ... poprzedni kod ...
10
11  feed(species, food) {
12    const diet = this.diets[species];
13
14    if (!diet) {
15      throw new Error('Unknown species');
16    }
17
18    if (diet === 'carnivore' && food !== 'meat') {
19      return { success: false, reason: 'Wrong diet' };
20    }
21    if (diet === 'herbivore' && food !== 'plants') {
22      return { success: false, reason: 'Wrong diet' };
23    }
24
25    return { success: true };
26  }
27}

Zasady TDD

  1. Nie pisz kodu produkcyjnego bez czerwonego testu - zawsze najpierw test
  2. Napisz minimum testu - tyle, ile potrzeba do pokazania błędu
  3. Napisz minimum kodu - tyle, ile potrzeba do przejścia testu
  4. Refaktoruj często - ulepszaj kod przy zielonych testach
  5. Małe kroki - każda iteracja dodaje jedną małą funkcjonalność

Korzyści TDD

1// TDD prowadzi do:
2const tddBenefits = {
3  betterDesign: 'Kod jest projektowany pod testy = lepszy design',
4  lessDebugging: 'Błędy wyłapywane natychmiast',
5  documentation: 'Testy dokumentują zachowanie kodu',
6  confidence: 'Pewność przy zmianach i refaktorze',
7  coverage: '100% pokrycia kodu testami z definicji'
8};
Ir a CodeWorlds