Programowanie sterowane testami z pomocą AI
Programowanie sterowane testami z pomocą AI oznacza napisanie najpierw testu, który nie przechodzi, potwierdzenie, że nie przechodzi z właściwego powodu, i dopiero potem wpuszczenie agenta do implementacji pod cel, którego nie da się oszukać. Pętla red-green-refactor dzieli się na cztery wąskie instrukcje — testy, potwierdzenie, implementacja, refaktoryzacja — każda w osobnej turze.
Prosisz agenta o zbudowanie rate limitera. Generuje coś, co wygląda poprawnie. Wdrażasz. Dwa dni później twoje API pada, bo rate limiter nie obsługuje poprawnie współbieżnych żądań — to wyścig, którego agent nigdy nie pomyślał przetestować, bo nikt mu nie powiedział, co znaczy „działający”.
Ta sama pułapka zamyka się z drugiej strony, gdy agent pisze testy i implementację w jednej turze. Prosisz o helper do liczenia rabatów, dostajesz kod plus przechodzący zestaw testów, a dwa dni później dział finansowy zgłasza, że zamówienia z wygasłymi kuponami wciąż dostają rabat. Testy pisane po kodzie najczęściej dowodzą jedynie, że kod robi to, co robi kod.
TDD odwraca obie sytuacje. Gdy testy powstają pierwsze, agent ma precyzyjną, maszynowo weryfikowalną definicję sukcesu: uruchamia zestaw, czyta niepowodzenia i iteruje, aż przejdą, bez miejsca na zgadywanie, o co ci chodziło. To pojedyncza technika o najwyższej dźwigni, jeśli chodzi o wiarygodne wyniki od asystenta AI — a krok „potwierdź czerwony” jest tym, co powstrzymuje agenta przed pisaniem testów przechodzących, które niczego nie sprawdzają.
Co daje TDD sterowane przez AI
Dział zatytułowany „Co daje TDD sterowane przez AI”- Powtarzalną pętlę red-green-refactor, którą prowadzisz z agentem, zamiast wpisywać każdą linię
- Prompt generujący kompletną specyfikację testów z listy wymagań oraz przykład wypełniony konkretem
- Gotowe do wklejenia prompty, które przypinają agenta do jednej fazy naraz (testy, potem kod, potem refaktoryzacja)
- Mechanikę Cursora / Claude Code / Codeksa pozwalającą zestawowi działać bez nadzoru między fazami
- Znaki, że agent osłabia twoje testy, by wymusić zielony wynik — i zabezpieczenia, które go powstrzymują
Pętla red-green-refactor z agentem
Dział zatytułowany „Pętla red-green-refactor z agentem”Klasyczny cykl to czerwony, zielony, refaktor. Z agentem każda faza staje się osobną, wąską instrukcją, a dyscyplina, dzięki której całość działa, sprowadza się do jednej reguły: nigdy nie pozwól, by ta sama tura napisała jednocześnie nieprzechodzący test i kod, który go zaspokaja.
- Napisz testy (czerwony). Określ zachowanie i przypadki brzegowe oraz wyraźnie zabroń implementacji. Opisujesz kontrakt, a nie prosisz o funkcję.
- Potwierdź niepowodzenie (czerwony). Niech agent uruchomi zestaw i pokaże ci niepowodzenia. To dowodzi, że testy celują w prawdziwe, niezaimplementowane zachowanie — a nie w literówkę w imporcie, która „nie przechodzi” z niewłaściwego powodu.
- Implementuj do przejścia (zielony). Jedna wąska instrukcja: spraw, by te testy przeszły, nie dotykaj plików testowych. Cel jest jednoznaczny i sprawdzalny maszynowo.
- Iteruj i refaktoryzuj. Agent uruchamia zestaw, czyta niepowodzenia i poprawia, aż będzie zielono. Gdy jest zielono, poproś o przebieg refaktoryzacji — testy są teraz siatką bezpieczeństwa, która czyni przebudowę tanią.
Kluczowe spostrzeżenie pod tą mechaniką: to test jest twoją specyfikacją. Dobrze napisany test komunikuje zamiar znacznie precyzyjniej niż jakikolwiek prompt w języku naturalnym — i właśnie dlatego przegląd, który ma znaczenie, dotyczy testów, a nie implementacji.
Zamiana wymagań w specyfikację testów
Dział zatytułowany „Zamiana wymagań w specyfikację testów”Zacznij od zachowania, którego chcesz, a nie od implementacji. Testy możesz napisać sam albo pozwolić agentowi naszkicować je z listy wymagań, ale to ty je przeglądasz i zatwierdzasz, zanim jakakolwiek implementacja zostanie autoryzowana. Konkretne wymagania dają konkretne testy; ogólnikowy prompt daje testy sprawdzające, że funkcja istnieje.
Ten prompt to wypełniona wersja szablonu poniżej. Po szablon sięgaj, gdy zaczynasz od dokumentu z wymaganiami i chcesz, by agent wyliczył przypadki; po prompty fazowe z następnej sekcji — gdy już wiesz, które zachowanie przypinasz.
Cztery fazy na prawdziwej metodzie serwisowej
Dział zatytułowany „Cztery fazy na prawdziwej metodzie serwisowej”Czysta funkcja w izolacji daje zgrabne demo i nie uczy niczego. Oto pętla na przypadku w kształcie produkcyjnym: metoda serwisowa ze ścieżkami błędów i zamockowaną zależnością.
Faza 1 — tylko testy. Przypnij model do pisania testów i niczego więcej:
Faza 2 — potwierdź czerwony. Nie pomijaj tego. Plik testowy importujący moduł, który jeszcze nie istnieje, powinien zawieść już przy rozwiązywaniu importu; test, który tu przechodzi, to test niczego nie sprawdzający.
Faza 3 — implementuj do zielonego. Dopiero teraz autoryzujesz implementację i odgradzasz testy:
To ostatnie zdanie jest kluczowe. Bez niego agent, który utknie, często „naprawi” nieprzechodzącą asercję zamiast kodu. Ta sama bariera, zapisana jako zestaw reguł wielokrotnego użytku zamiast wiązania z jednym serwisem, to prompt, który warto trzymać w pliku ze snippetami:
Faza 4 — refaktoryzacja pod zielonym. Gdy zestaw przechodzi, masz kontrakt, który pozwala bezpiecznie przebudować kod:
Prowadzenie pętli w każdym narzędziu
Dział zatytułowany „Prowadzenie pętli w każdym narzędziu”Fazy są wszędzie identyczne. Różni się to, jak każde narzędzie uruchamia zestaw i ile pętli test-napraw-przetestuj wykona bez nadzoru.
Użyj trybu Agent i pozwól mu samodzielnie uruchamiać testy. W Settings -> Cursor Settings -> Agents -> Auto-Run ustaw Auto-Run Mode na Run in Sandbox (na macOS/Linux), aby polecenia wykonywały się automatycznie w piaskownicy bez pytania — to ścieżka pracy bez nadzoru, którą zaleca Cursor. Następnie dodaj npx vitest (lub npm test) do Command Allowlist, by runner testów uruchamiał się natychmiast nawet poza piaskownicą. Unikaj trybu Run Everything w pętli bez nadzoru: własne wskazówki dotyczące bezpieczeństwa Cursora mówią, by nigdy go nie używać, ponieważ pomija on wszystkie zabezpieczenia.
Trzymaj fazy jako osobne tury czatu — punkty kontrolne pozwalają cofnąć się do czerwonego, jeśli zielona faza pójdzie nie tak, a cofnięcie bije debugowanie do przodu, gdy agent zepsuje coś, co wcześniej przechodziło. Obserwuj widok diff: jeśli edycja z fazy zielonej dotyka pliku *.test.ts, odrzuć ją.
Prowadź go z REPL albo skryptuj bezgłowo. Interaktywnie wklejaj kolejno każdy prompt fazy i pozwól Claude uruchamiać npm test. Aby pętla sama się korygowała, dodaj hook PostToolUse (matcher Edit|Write) w .claude/settings.json, który po każdej edycji ponownie uruchamia zestaw, dzięki czemu Claude od razu widzi niepowodzenia:
{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "npx vitest run --reporter=dot" }] } ] }}Dla jednorazowej fazy zielonej w CI lub w skrypcie uruchom go bezgłowo:
claude -p "Implement src/services/pricing.ts so the suite passes. Run 'npx vitest run' and iterate until green. Do not edit any *.test.ts file." --allowedTools "Read,Edit,Write,Bash"Jeśli Claude utknie w pętli nieprzechodzących testów, zrób /clear i zacznij nową sesję z promptem opisującym konkretne niepowodzenie, zamiast powtarzać całe zadanie.
W TUI skonfiguruj jawnie sandbox workspace i interaktywną politykę zatwierdzania. Rutynowe edycje i polecenia testowe dozwolone przez sandbox mogą wtedy iterować bez monitu:
codex --sandbox workspace-write -c approval_policy=on-request \ "Implement src/services/pricing.ts to pass tests in src/services/pricing.test.ts. Run 'npx vitest run' and iterate until green. Don't touch the test files."on-request pyta tylko wtedy, gdy Codex chce przekroczyć granicę sandboxa; nieprzechodzący test to zwykły wynik narzędzia, a nie zdarzenie zatwierdzania. Fazę czerwoną i zieloną uruchamiaj w osobnych wątkach, żeby rozmowa o generowaniu testów nie zaśmiecała kontekstu implementacji. Do równoległych eksperymentów red/green — dwóch implementacji wobec tych samych zablokowanych testów — utwórz osobny git worktree na każdy przebieg, by zestawy nie kolidowały.
Wychwytywanie antywzorca osłabiania testów
Dział zatytułowany „Wychwytywanie antywzorca osłabiania testów”Najgroźniejszy tryb awarii w TDD wspomaganym AI to agent modyfikujący twoje testy, żeby przeszły, zamiast naprawiać implementację. Rzadko wygląda to dramatycznie. Zwracaj uwagę na:
- Asercje stające się mniej konkretne.
expect(result).toBe(429)zamienia się wexpect(result).toBeDefined(). - Znikające testy. Agent usuwa „niestabilny” test zamiast naprawić kod. Przechodzący zestaw, który się skurczył, to sygnał ostrzegawczy — porównaj diff plików testowych, zanim zaufasz zielonemu przebiegowi.
- Mocki zastępujące prawdziwe zachowanie. Agent mockuje dokładnie to, po co ten test istniał.
Gdy zestaw jest zielony i uczciwy, wzmacnianie pokrycia to osobny przebieg — a testy, które w nim powstają, mają nie przechodzić:
Gdzie TDD sterowane przez AI się psuje
Dział zatytułowany „Gdzie TDD sterowane przez AI się psuje”- Testy przechodzą, zanim kod istnieje. Jeśli „potwierdź czerwony” wraca na zielono, testy nie ćwiczą celu — zwykle zamockowana zależność zwraca prawdziwą wartość domyślną albo import rozwiązuje się do zaślepki. Nigdy nie pomijaj kroku potwierdzania czerwonego.
- Agent generuje trywialne testy. Ogólnikowy prompt („write tests for this function”) kupuje testy sprawdzające, że funkcja istnieje i coś zwraca. Nazwij zachowania i dołącz konkretne przykłady wejść i wyjść.
- Testy przyklejone do implementacji. Jeśli testy sprawdzają wywołania prywatnych metod albo wewnętrzne struktury danych, agent nie może swobodnie refaktoryzować. Pisz pod publiczne API i oczekiwane zachowanie.
- Niestabilne testy asynchroniczne „naprawiane” przez sleep. Gdy testy zależne od czasu zawodzą sporadycznie, agenci uwielbiają zamaskować to przez
setTimeout/sleep. Promptuj o deterministyczną kontrolę: “use fake timers (vi.useFakeTimers()) and advance them explicitly; do not add real delays.” - Zestaw jest zbyt wolny, by go zapętlać. TDD z agentem działa, gdy testy uruchamiają się w sekundach. Podczas pętli zawęź runner do pliku poddawanego testom (
npx vitest run src/services/pricing.test.ts), a na końcu uruchom całość raz. Jest 30+ używa--testPathPatterns=rateLimiter(pojedyncza forma--testPathPatternteraz ostrzega); Vitest i Mocha przyjmują argument ścieżki albo--grep. - Za dużo testów pisanych z góry. Zacznij od trzech do pięciu testów pokrywających podstawowe zachowanie i pozwól implementacji ujawnić, które przypadki brzegowe są ważne. Trzydzieści testów przed jakąkolwiek implementacją to paraliż analityczny z dodatkowymi krokami.