Przejdź do głównej zawartości

Test-driven development w Cursorze

Test-driven development w Cursorze polega na tym, że agent najpierw pisze czerwony zestaw testów, programista zatwierdza go jako specyfikację, a potem agent implementuje kod, aż testy przejdą, nie zmieniając ich. Kontrola niezmienności testów, testy mutacyjne i bramki CI dowodzą wyniku, więc przegląd skupia się na testach, a nie na każdej linii implementacji.

Potrzebujesz konwertera markdown-do-HTML dla swojego CMS-a. Zagnieżdżone listy, bloki kodu w cytatach i encje HTML sprawiają, że agent rzadko trafia we wszystkie przypadki za pierwszym razem, a pierwsza wersja agenta wygląda wiarygodnie. Czytanie 400 linii parsera w poszukiwaniu trzech błędnych gałęzi to dokładnie ta praca, której chcesz się pozbyć. Ta strona jest dla programistów, którzy na co dzień pracują z agentem Cursora, oraz dla tech leadów, którzy chcą, żeby ta sama pętla działała powtarzalnie w całym zespole.

  • Dwa prompty, które czysto dzielą pracę: jeden pisze wyłącznie testy i zaślepkę, drugi implementuje bez prawa do ich zmiany.
  • Sprawdzenie z dwóch poleceń (git diff względem otagowanego commita „red” oraz git status), które dowodzi, że agent nie osłabił testów, żeby dojść do zielonego.
  • Krok testów mutacyjnych ze Strykerem, który pokazuje, czy zatwierdzone testy w ogóle potrafią złapać błąd.
  • Prompt bramki przed PR (typy, lint, testy, progi pokrycia) oraz prompt zamieniający zgłoszenie błędu w test regresyjny.
  • Sposoby wyjścia z pięciu typowych awarii tej pętli.

Zasadę stojącą za pętlą, niezależną od narzędzia, opisuje strona test-driven development ze wsparciem AI. Ta strona to procedura specyficzna dla Cursora.

Pętla ma jedną regułę, którą chroni cała reszta: testy są kontraktem i tylko ty zmieniasz kontrakt. Agent pisze pierwszą wersję testów, ty je przeglądasz i commitujesz, a od tej chwili agent pracuje wyłącznie nad implementacją.

  1. Każ agentowi napisać testy i zaślepkę, nic więcej. Zaślepka rzucająca Not implemented utrzymuje zestaw na czerwono z właściwego powodu i ustala sygnaturę funkcji, zanim powstanie jakakolwiek logika.

  2. Sam przejrzyj testy, a potem je zacommituj. Właśnie tu czytasz uważnie. Czytasz 40 krótkich asercji zamiast 400 linii parsera. Zacommituj je jako osobny commit „red” i otaguj go, żeby mieć stały punkt odniesienia.

  3. Każ agentowi implementować do zielonego, z testami tylko do odczytu. Agent uruchamia zestaw w swoim terminalu, czyta błędy i iteruje.

  4. Udowodnij, że testy nie zostały ruszone, a potem że są wystarczająco mocne. Uruchom kontrolę niezmienności testów i zawężony przebieg mutacyjny.

  5. Uruchom bramkę przed PR i otwórz pull request. CI powtarza te same kontrole, więc nic nie zależy od twojej maszyny.

Otwórz agenta w nowym czacie i wklej poniższy prompt. Wskazuje Vitest i układ plików; zmień te dwa szczegóły pod swój projekt.

Teraz przeczytaj plik testów. Sprawdzasz trzy rzeczy: każde zachowanie, na którym ci zależy, ma dokładny oczekiwany string; oczekiwane stringi to naprawdę to, czego chcesz (agent mógł zakodować zgadywanie, z którym się nie zgadzasz); żaden test nie sprawdza czegoś, co gwarantuje już system typów. Popraw testy ręcznie albo poproś o zmiany. Gdy testy mówią dokładnie to, czego chcesz, zacommituj je i otaguj commit:

Okno terminala
# Terminal, w katalogu głównym repozytorium
git add src/lib/markdown
git commit -m "test: markdownToHtml specification (red)"
git tag tdd-red

Tag tdd-red oznacza zatwierdzony kontrakt. Każda późniejsza kontrola porównuje z tagiem, a nie z HEAD, więc commit zrobiony po commicie „red” nie ukryje osłabionego testu.

Zacznij nowy czat, żeby implementacja nie opierała się na rozumowaniu, z którego powstały testy. Agent uruchamia npx vitest run w swoim terminalu przy każdej iteracji. To, czy Cursor pyta cię przed każdym poleceniem, zależy od trybu uruchamiania ustawionego w twoim workspace (dokumentacja Cursora, Agent security → Run modes, sprawdzone 2026-08-28). Jeśli twój tryb uruchamiania pozwala zatwierdzać konkretne polecenia, zatwierdź tylko polecenie testów, zamiast wyłączać zatwierdzanie dla wszystkiego.

Ograniczenie 6 jest ważniejsze, niż się wydaje. Bez warunku stopu agent łata pojedyncze przypadki, a parser obrasta gałęzią na każdy test. Skill /loop w Cursorze też potrafi powtarzać prompt aż do osiągnięcia wyniku (pojawił się w Cursorze 3.5, 2026-05-20); jeśli go używasz, zostaw w prompcie ten sam warunek stopu.

Nie ufaj podsumowaniu agenta, że zostawił testy w spokoju. Sprawdź to:

Okno terminala
# Terminal: kończy się kodem 1 i wypisuje diff, jeśli śledzony plik testów różni się od otagowanego kontraktu
git diff --exit-code tdd-red -- src/lib/markdown/__tests__/
# Nie powinno nic wypisać: pokazuje nowe nieśledzone lub niezacommitowane pliki w katalogu testów
git status --porcelain src/lib/markdown/__tests__/

git diff względem tagu łapie osłabiony test nawet wtedy, gdy został już zacommitowany, ale nie widzi nowych, nieśledzonych plików; git status --porcelain je widzi. Jeśli któreś z poleceń wypisze coś, czego nie zatwierdziłeś, odrzuć zmianę, przywróć testy przez git checkout tdd-red -- src/lib/markdown/__tests__/, usuń nowe pliki testów z listy i ponownie uruchom prompt implementacji. Gdy zatwierdzasz dodatkowe testy (testy mutantów albo test regresyjny opisane niżej), zacommituj je i przesuń tag poleceniem git tag -f tdd-red, żeby zatwierdzony zestaw stał się nowym kontraktem. Żeby reguła obowiązywała cały zespół, dodaj ją jako regułę projektu: „When asked to implement against existing tests, never modify files under __tests__/; report a suspected wrong test instead.” Reguła to instrukcja, nie ogrodzenie. Warstwą, która naprawdę egzekwuje zasadę, jest kontrola, której agent nie może edytować: hook Cursora, który sprawdza edycje ścieżek testów i odrzuca turę, albo zadanie CI porównujące diff testów z zatwierdzonym commitem. Oba wzorce pokazuje strona ochrona wyroczni.

Zielone testy, które zatwierdziłeś, są warte tyle, co ich asercje. Testy mutacyjne wprowadzają do implementacji drobne zmiany (odwracają porównanie, usuwają linię) i sprawdzają, czy przy każdej z nich pada co najmniej jeden test. Mutant, który przeżył, to błąd, który twój zestaw przepuściłby na produkcję.

Okno terminala
# Terminal: Stryker 10.0.0 z runnerem Vitest (npm, sprawdzone 2026-09-26)
npm install --save-dev @stryker-mutator/core @stryker-mutator/vitest-runner
npx stryker run --testRunner vitest --mutate src/lib/markdown/markdown-to-html.ts --incremental

Zawęź --mutate do plików, które zmieniłeś; przebieg po całym repozytorium jest wolny i niewiele mówi o tej zmianie. --incremental zapisuje wyniki, więc kolejny przebieg testuje ponownie tylko zmieniony kod. Potem oddaj ocalałe mutanty agentowi:

Nowe testy trafiają do osobnego pliku, żebyś mógł przejrzeć je jako całość, zanim dołączą do zablokowanego zestawu: po zatwierdzeniu zacommituj plik i uruchom git tag -f tdd-red. Jak wysokiego wyniku wymagać, zanim pętla zacznie działać bez nadzoru, opisuje strona jak silna jest twoja wyrocznia.

Każde zgłoszenie błędu zamieniaj najpierw w czerwony test

Dział zatytułowany „Każde zgłoszenie błędu zamieniaj najpierw w czerwony test”

Ten sam podział dotyczy poprawek błędów. Test regresyjny powstaje i zostaje potwierdzony na czerwono, zanim ktokolwiek zacznie szukać przyczyny, więc poprawka jest udowodniona i taka pozostaje.

Gdy zatwierdzisz test, zacommituj go, przesuń tag poleceniem git tag -f tdd-red i uruchom ponownie prompt implementacji do zielonego. Przy błędach sporadycznych i takich, których jeszcze nie umiesz odtworzyć, zacznij od procedury z debugowania w Cursorze.

Szybkie kontrole idą pierwsze, żeby błąd typów nie kosztował pełnego przebiegu pokrycia. Samo pokrycie linii to słaby sygnał, więc bramka ustawia też próg dla gałęzi.

Krok pokrycia wymaga zainstalowanego @vitest/coverage-v8 (5.0.2 w npm, sprawdzone 2026-09-26, zgodne z Vitest 5.0.2). Przebieg z pokryciem uruchamia też wszystkie testy, więc osobne zwykłe vitest run tylko by go powtórzyło. CI musi uruchamiać te same trzy polecenia; lokalny przebieg agenta to wygoda, a CI/CD z Cursorem pokazuje, jak wpiąć je w workflow.

Weryfikacja jest podzielona tak, żeby nikt nie czytał każdej linii implementacji i nikt nie musiał ufać raportowi samego agenta.

KontrolaCzego dowodziKto za nią odpowiada
Przegląd pliku testów przed commitem „red”Kontrakt mówi to, czego chceszProgramista, który zlecił zmianę
git diff --exit-code tdd-red i git status --porcelain na ścieżkach testówAgent nie przepisał kontraktuProgramista lokalnie, CI w pull requeście
Wynik mutacyjny dla zmienionych plikówKontrakt wykrywa błędną implementacjęProgramista; poprzeczkę ustala tech lead
Typy, lint, testy, progi pokryciaZmiana jest spójna z resztą koduCI, jako wymagany status check
Przegląd pull requestaZmiany w testach i publicznym API są zamierzoneRecenzent, który czyta najpierw testy; code review w Cursorze opisuje Bugbota jako drugiego czytelnika

Recenzent czyta diff testów linia po linii, a diff implementacji tylko w wyjątkach: tam, gdzie ocalałe mutanty oznaczono jako równoważne, gdzie pokrycie jest poniżej progu i gdzie zmieniła się publiczna sygnatura.

Działa dobrzeDziała słaboZamiast tego
Parsery, formatery, walidatory, reguły cenoweUkład wizualny („czy to dobrze wygląda”)Kontrole zrzutów ekranu lub w przeglądarce; zob. wzorce testowania w Cursorze
Handlery API z kontraktem żądania i odpowiedziOrkiestracja, w której przygotowanie przerasta kodTesty integracyjne z prawdziwymi usługami
Poprawki błędów odtwarzalnych jednym wejściemOptymalizacja wydajnościBenchmarki z budżetem; same testy poprawności przepuszczą wolny kod
Czyste transformacje danychKod legacy bez żadnych testówNajpierw testy charakteryzacyjne, potem TDD dla zmiany

Przy kodzie z bogatą przestrzenią wejść testy oparte na przykładach zachęcają agenta do dopasowania się do przykładów. Niezmienniki sprawdzane testami opartymi na właściwościach (na przykład z fast-check 4.10.2) trudniej obejść.

Jeśli uruchamiasz tę pętlę codziennie, dyscyplinę red-green-refactor może nieść skill zamiast wklejanego promptu. Skill tdd z repozytorium mattpocock/skills Matta Pococka to najczęściej instalowany skill TDD na skills.sh: 966 524 instalacje w migawce z 2026-09-26 (SECONDARY: odczyt z migawki wyszukiwarki, bo skills.sh nie był dostępny bezpośrednio).

Okno terminala
# Terminal, katalog główny repozytorium: instaluje do .agents/skills/ dla Cursora (skills CLI 1.7.0)
npx skills add mattpocock/skills --skill tdd -a cursor

Potem poproś agenta: „Fix the rounding bug in invoice totals test-first.” Skill każe mu napisać czerwony test, uruchomić go, wprowadzić minimalną poprawkę, potwierdzić, że test przechodzi, i zrefaktoryzować. Skill to instrukcje od strony trzeciej, działające z twoimi uprawnieniami, więc przed instalacją przeczytaj jego SKILL.md (bezpieczeństwo skilli). Skill nie zastępuje opisanej wyżej kontroli niezmienności testów: mówi agentowi, jak ma się zachować, ale nie dowodzi, że tak się zachował. Więcej opcji, w tym test-driven-development z Superpowers, porównuje strona najczęściej instalowane skille praktyk inżynierskich.

Co się psuje, gdy agent Cursora prowadzi pętlę TDD?

Dział zatytułowany „Co się psuje, gdy agent Cursora prowadzi pętlę TDD?”

Agent edytuje testy, żeby dojść do zielonego. To najgroźniejsza awaria, bo wynik wygląda na sukces. Objawy: mniejsza liczba testów, toBe poluzowane do toContain, dopisane .skip. Wyjście: łapie to kontrola git diff --exit-code; przywróć testy z tagu tdd-red, zacznij nowy czat i ponownie uruchom prompt implementacji. Jeśli to się powtarza, przenieś egzekwowanie z promptu do hooka albo kontroli w CI.

Agent robi przypadki specjalne pod wejścia z testów. Implementacja zawiera gałąź dopasowaną do dosłownego stringa z testu. Wychwytują to testy mutacyjne i przegląd kodu, a szybkim detektorem jest garść niewidzianych wejść: poproś agenta o dziesięć nowych próbek markdownu, niech przewidzi HTML, potem je uruchom. Wyjście: dodaj testy oparte na właściwościach albo odłożony zbiór przykładów (holdout), którego agent nigdy nie widzi.

Testy przechodzą dla dowolnej implementacji. Asercje w rodzaju toBeDefined() albo typeof result === 'string' nie mogą paść. Wyjście: prompt „najpierw testy” ich zabrania, a przebieg Strykera ujawnia mutanty, które przez nie przeżyły.

Testy są związane z wnętrzem implementacji. Testy szpiegujące prywatne funkcje pomocnicze pękają przy każdym refaktoringu i pchają agenta do zachowania złej struktury. Wyjście: przepisz je tak, żeby testowały tylko publiczną funkcję, i trzymaj zasadę „test public behaviour only” w regule projektu.

Pętla staje w miejscu. Liczba błędów przestaje spadać po trzech, czterech iteracjach, zwykle dlatego, że pozostałe testy ujawniają wadę projektu (parser linia po linii nie obsłuży zagnieżdżeń). Wyjście: zatrzymaj czat, przełącz się na Plan Mode i poproś o analizę padających testów oraz propozycję innego projektu, zanim powstanie kolejna linia kodu. Zatwierdź plan, a potem uruchom prompt implementacji w nowym czacie.

Pracujesz w Codeksie? Wersję tej pętli dla Codeksa opisuje strona testowanie z Codeksem.