November 29, 2025 (10mo ago) — last updated August 28, 2026 (1mo ago)

Red‑Green‑Refactor TDD — praktyczny przewodnik

Opanuj cykl Red‑Green‑Refactor TDD: workflow, przykład w React/TypeScript i porady, by tworzyć czytelniejszy i łatwiejszy w utrzymaniu kod.

← Back to blog
Cover Image for Red‑Green‑Refactor TDD — praktyczny przewodnik

Cykl Red‑Green‑Refactor w TDD to praktyczne podejście do projektowania i dostarczania oprogramowania. Ten przewodnik pokazuje workflow, przykłady z React/TypeScript i praktyczne porady, które pomogą twojemu zespołowi pisać czystszy, łatwiejszy w utrzymaniu kod.

Red‑Green‑Refactor TDD: Praktyczny przewodnik

Podsumowanie: Opanuj cykl Red‑Green‑Refactor TDD dzięki praktycznemu workflow, przykładom i korzyściom biznesowym, aby tworzyć czystszy, łatwiejszy w utrzymaniu kod.

Wprowadzenie

Cykl Red‑Green‑Refactor w Test‑Driven Development (TDD) to zdyscyplinowana praktyka projektowa, która redukuje niepewność i przyspiesza bezpieczne zmiany. W tym artykule poznasz zasady workflow, praktyczny przykład z React i TypeScript oraz porady, jak wprowadzić TDD w zespole, także przy pracy z kodem legacy.

Czym jest Red‑Green‑Refactor?

Red‑Green‑Refactor to prosta pętla pracy:

  • Red: napisz mały, automatyczny test, który nie przechodzi.
  • Green: wprowadź najmniejszy możliwy kod, by test przeszedł.
  • Refactor: oczyść implementację przy zachowaniu działających testów.

To podejście zmusza do projektowania z punktu widzenia użytkownika kodu, umożliwia częste refaktoryzacje i ogranicza dług techniczny.

Kluczowe korzyści

  • Lepsza jakość kodu dzięki szybkiemu wykrywaniu regresji.
  • Wykonywalna dokumentacja — zestaw testów pokazuje oczekiwane zachowanie.
  • Pewność przy refaktoryzacji i szybsze wdrażanie zmian.

Wiele zespołów już włączyło TDD do praktyk wytwarzania oprogramowania1, a eksperymentalne badania wskazują na mierzalne korzyści jakościowe2.

Rytm pracy: szczegóły etapów

Każdy krok ma konkretny cel i powinien być krótki oraz weryfikowalny.

  • Red (test nieprzechodzący): zdefiniuj najmniejsze zachowanie użytkowe.
  • Green (kod przechodzący test): napisz najprostszy kod, który spełnia test.
  • Refactor (porządki): usuń duplikację, popraw nazwy i strukturę bez zmiany zachowania.

Pominięcie refaktoryzacji prowadzi do narastania długu technicznego.

Red‑Green‑Refactor w praktyce — przykład z React i TypeScript

Pokażemy prosty przepływ dla komponentu LikeButton z użyciem React, TypeScript i Jest.

Faza Red: zdefiniuj pierwszy wymóg

Najprostszy test: komponent renderuje przycisk z etykietą “Like”.

// LikeButton.test.tsx
import React from "react";
import { render, screen } from "@testing-library/react";
import LikeButton from "./LikeButton";

describe("LikeButton", () => {
  it("renders a button with the initial text “Like”", () => {
    render(<LikeButton />);
    const likeButton = screen.getByRole("button", { name: /like/i });
    expect(likeButton).toBeInTheDocument();
  });
});

Uruchomienie testu zakończy się niepowodzeniem, co jest oczekiwanym pierwszym krokiem.

Faza Green: wystarczająco, by test przeszedł

Utwórz minimalny komponent:

// LikeButton.tsx
import React from "react";

const LikeButton = () => {
  return <button>Like</button>;
};

export default LikeButton;

Testy stają się zielone — działamy iteracyjnie.

Faza Refactor: popraw implementację

Dodaj typy i przygotuj komponent na przyszłe rozszerzenia:

// LikeButton.tsx (refactored)
import React, { FC } from "react";

type LikeButtonProps = {};

const LikeButton: FC<LikeButtonProps> = () => {
  return <button>Like</button>;
};

export default LikeButton;

Testy nadal przechodzą, co daje pewność przy dalszych zmianach.

Iteracja: obsługa kliknięcia

Nowy wymóg: po kliknięciu tekst zmienia się na “Liked” i przycisk staje się wyłączony. Najpierw test:

// LikeButton.test.tsx
it('changes text to “Liked” and becomes disabled when clicked', () => {
  render(<LikeButton />);
  const likeButton = screen.getByRole("button", { name: /like/i });
  fireEvent.click(likeButton);
  expect(likeButton).toHaveTextContent("Liked");
  expect(likeButton).toBeDisabled();
});

Implementacja minimalna:

// LikeButton.tsx
import React, { FC, useState } from "react";

type LikeButtonProps = {};

const LikeButton: FC<LikeButtonProps> = () => {
  const [liked, setLiked] = useState(false);
  const handleClick = () => setLiked(true);
  return (
    <button onClick={handleClick} disabled={liked}>
      {liked ? "Liked" : "Like"}
    </button>
  );
};

export default LikeButton;

Powtarzaj: mała, testowana zmiana na raz.

Argument biznesowy za jakością kodu

Korzyści techniczne TDD przekładają się na niższe koszty utrzymania i mniejsze ryzyko produkcyjne. Zespoły dbające o automatyczne testy rzadziej doświadczają regresji i szybciej wdrażają zmiany. Raporty dotyczące praktyk DevOps pokazują, że zespoły o wysokiej wydajności mają znacząco niższy wskaźnik niepowodzeń zmian i krótszy czas wdrożenia3.

Pisanie testów przed kodem pomaga kontrolować zakres zmian i zapobiega dodawaniu niepotrzebnej funkcjonalności, co w dłuższej perspektywie obniża całkowity koszt posiadania oprogramowania2.

Typowe pułapki i jak ich unikać

Testy integracyjne udające jednostkowe

Problem: test jest powolny i kruchy, bo dotyka zewnętrznych zależności.

Rozwiązanie: izoluj moduł, używaj mocków i stubów; testy integracyjne trzymaj w osobnym zestawie.

Testowanie implementacji zamiast zachowania

Problem: testy sprawdzają wewnętrzne szczegóły i łamią się przy refaktoryzacji.

Rozwiązanie: testuj publiczne API i obserwowalne efekty; opisuj oczekiwane zachowanie.

Pomijanie refaktoryzacji

Problem: kod rośnie w nieuporządkowany sposób.

Rozwiązanie: traktuj refactor jako obowiązkowy krok po tym, jak testy przeszły.

Integracja TDD w zespole i z kodem legacy

Wdrożenie TDD to zmiana kulturowa. Warto włączyć kryteria testów do Definicji Ukończenia i promować praktyki takie jak pair programming czy mob programming.

Testy charakterystyczne dla kodu legacy

Gdy baza kodu jest nietestowana, napisz characterization tests, które dokumentują obecne zachowanie i umożliwiają bezpieczną refaktoryzację.

Automatyzacja w CI/CD

Uruchamiaj testy przy każdym commicie i ustaw bramki jakości przed mergowaniem. Automatyzacja zapewnia szybką informację zwrotną i chroni główny branch.

Najczęstsze pytania o TDD — krótkie odpowiedzi

Czy TDD zastępuje inne rodzaje testów?

Nie. TDD to podejście skupione na testach jednostkowych jako narzędziu projektowym; nadal potrzebne są testy integracyjne i end‑to‑end.

Jak stosować TDD przy integracji z bazą danych i API?

Izoluj logikę biznesową od zależności zewnętrznych, używaj mocków w testach jednostkowych i oddzielnego zestawu testów integracyjnych.

Czy warto testować proste komponenty UI?

Tak, gdy testujesz zachowanie widoczne dla użytkownika, nie wewnętrzne detale implementacji.

Trzy praktyczne pytania i odpowiedzi

Jak zacząć z TDD na małym projekcie?

Zacznij od jednej nowej funkcji: wymagaj nieudanego testu przed implementacją i zawsze wykonuj refactor po przejściu testów.

Czy TDD spowalnia rozwój?

Początkowo tempo może się wydawać niższe, ale szybko zyskujesz na przewidywalności i niższych kosztach naprawy błędów, co przyspiesza dostarczanie w dłuższej perspektywie.

Jak przekonać zespół do TDD?

Pokaż szybkie, mierzalne korzyści: mniejsze regresje, krótszy czas debugowania i łatwiejsze wdrożenie nowych osób. Zademonstruj to na jednym module lub zadaniu.

Zakończenie

Red‑Green‑Refactor to prosty rytm, który pomaga projektować bezpieczne zmiany i utrzymywać wysoką jakość kodu. Zacznij od małych kroków, traktuj testy jako dokumentację i egzekwuj refaktoryzację każdej przejściowej iteracji.

1.
Digital.ai, “State of Agile Report,” Digital.ai, https://digital.ai/resource-center/state-of-agile-report
2.
Basili, Victor R., i in., “An Empirical Study on the Effects of Test-Driven Development,” https://link.springer.com/article/10.1007/s10664-015-9378-2
3.
Google Cloud, “State of DevOps Report,” https://cloud.google.com/devops/state-of-devops
4.
Martin Fowler, “TestDrivenDevelopment,” https://martinfowler.com/bliki/TestDrivenDevelopment.html
← Back to blog
🙋🏻‍♂️

AI pisze kod.
Ty sprawiasz, że przetrwa.

W erze przyspieszenia AI czysty kod to nie tylko dobra praktyka — to różnica między systemami, które się skalują, a bazami kodu, które zapadają się pod własnym ciężarem.