Test-driven development w Cursor
Budujesz konwerter markdown-do-HTML dla swojego CMS. Wiesz z doświadczenia, że żadne AI nie potrafi konsekwentnie wygenerować tego za jednym razem — jest zbyt wiele przypadków brzegowych z zagnieżdżonymi listami, blokami kodu wewnątrz cytatów blokowych i encjami HTML. Tradycyjne podejście: napisz kod, przetestuj ręcznie, znajdź buga, napraw go, znajdź kolejnego buga, napraw tamtego, powtarzaj aż przestaniesz znajdować błędy (co nie oznacza, że ich nie ma).
Jest lepszy sposób. Napisz najpierw testy. Daj Cursor testy i poproś, aby implementował kod, dopóki wszystkie testy nie przejdą. Potem dodaj więcej testów przypadków brzegowych i pozwól mu iterować znowu. To TDD na autopilocie — ty definiujesz, co oznacza “poprawne”, a AI wymyśla, jak tam dojść. Steve Sewell z Builder.io opisuje to jako “tysiąc razy lepsze” niż tradycyjny workflow z AI i ma rację. AI przestaje być generatorem kodu, który potrzebuje QA, i staje się generatorem kodu, który sam sobie robi QA.
Co wyniesiesz z tej lekcji
Dział zatytułowany „Co wyniesiesz z tej lekcji”- Workflow TDD, w którym Cursor najpierw pisze testy, potem implementuje i iteruje, aż wszystko jest zielone
- Szablon promptu do generowania zestawów testów bogatych w przypadki brzegowe, które łapią bugi typowo wprowadzane przez AI
- Technikę iteracyjnego rozszerzania istniejących zestawów testów przez wklejanie awarii produkcyjnych jako nowe test case’y
- Prompt walidacji pre-PR, który uruchamia build, lint i zestaw testów, a następnie automatycznie naprawia awarie
- Zrozumienie, kiedy TDD z AI działa znakomicie, a kiedy się sypie
Workflow
Dział zatytułowany „Workflow”Krok 1: Zdefiniuj kontrakt testami
Dział zatytułowany „Krok 1: Zdefiniuj kontrakt testami”Zacznij od tego, że Agent pisze zestaw testów. Kluczowy insight: testy napisane przed implementacją to specyfikacja, nie tylko weryfikacja. Definiują dokładnie, co kod powinien robić.
Agent utworzy kompleksowy plik testowy z 30-50 test case’ami i stubową implementację. Uruchom testy — wszystkie powinny padać (poza potencjalnie testami dla pustego/białego inputu).
Krok 2: Implementuj, aż wszystkie testy przejdą
Dział zatytułowany „Krok 2: Implementuj, aż wszystkie testy przejdą”Teraz oddaj testy Agentowi i poproś go o implementację kodu. Z włączonym auto-run, Agent napisze kod, uruchomi testy, zobaczy awarie i naprawi je w pętli.
Tu auto-run (czyli to, co wcześniej nazywano “YOLO mode”) się opłaca. Agent uruchomi vitest, zobaczy 40 awarii, naprawi najczęstsze problemy parsowania, uruchomi ponownie, zobaczy 15 awarii, naprawi tamte i tak dalej. Monitorujesz postęp, ale nie musisz interweniować, chyba że utknie w pętli.
Krok 3: Dodaj przypadki brzegowe z rzeczywistych awarii
Dział zatytułowany „Krok 3: Dodaj przypadki brzegowe z rzeczywistych awarii”Gdy wszystkie początkowe testy przejdą, dodaj więcej testów opartych na markdownie, jaki faktycznie spotykasz na produkcji. Tu wklejasz prawdziwe treści, które zepsuły poprzednie implementacje.
@src/lib/markdown/__tests__/markdown-to-html.test.ts
Dodaj te dodatkowe test case'y oparte na rzeczywistym markdownie, który widzieliśmy psuć parsery:
1. Blok kodu zawierający składnię markdown (NIE powinien być parsowany)2. Link wewnątrz nagłówka: ## [Kliknij tutaj](url)3. Kolejne cytaty blokowe z różną zawartością4. Element listy rozciągający się na wiele linii z wcięciem kontynuacji5. Obraz wewnątrz linka: [](link-url)6. Mieszany HTML i markdown: <div>**pogrubienie wewnątrz div**</div>7. Składnia tabel ze wskaźnikami wyrównania (|:---|:---:|---:|)
Uruchom testy. Jeśli jakiekolwiek nowe testy padają, napraw implementację i iteruj, aż wszystkie przejdą.Krok 4: Pętla walidacji pre-PR
Dział zatytułowany „Krok 4: Pętla walidacji pre-PR”To jest workflow, który wiąże testowanie z twoim codziennym cyklem deweloperskim. Przed otwarciem każdego PR, poproś Agenta o uruchomienie pełnego zestawu walidacji i naprawienie tego, co znajdzie.
Ten jeden prompt zastępuje ręczną listę kontrolną pre-PR. Uruchamia szybkie sprawdzenia najpierw (kompilacja TypeScript, lint) przed wolniejszymi (zestaw testów, pokrycie) i naprawia problemy w miarę ich znajdowania. Niektórzy programiści uruchamiają to na końcu każdej sesji kodowania.
Krok 5: Naprawy błędów sterowane testami
Dział zatytułowany „Krok 5: Naprawy błędów sterowane testami”Gdy przychodzi zgłoszenie błędu, zamień je w padający test, zanim zaczniesz badać poprawkę. To gwarantuje, że błąd jest naprawiony i pozostaje naprawiony.
Zgłoszenie błędu: Gdy użytkownik wkleja markdown z zakończeniami linii w stylu Windows (\r\n),konwerter produkuje output HTML z podwójnymi odstępami.
1. Napisz padający test, który odtwarza dokładnie to zachowanie2. Uruchom go, aby potwierdzić, że pada3. Napraw implementację4. Uruchom WSZYSTKIE testy, aby potwierdzić, że poprawka nie psuje niczego innego5. Test powinien pozostać w zestawie na stałe jako ochrona przed regresjąKrok 6: Testowanie komponentów dla kodu UI
Dział zatytułowany „Krok 6: Testowanie komponentów dla kodu UI”TDD działa też dla komponentów React, choć pętla feedbacku jest nieco inna.
@src/components/data-table.tsx
Napisz testy dla komponentu DataTable za pomocą vitest i @testing-library/react:
1. Renderuje prawidłową liczbę wierszy z propa data2. Kliknięcie nagłówka kolumny wywołuje onSort z nazwą kolumny3. Pokazuje stan pusty, gdy data jest pustą tablicą4. Pokazuje wiersze skeleton, gdy isLoading jest true5. Stosuje prawidłowy atrybut aria-sort do posortowanej kolumny6. Nawigacja klawiaturą: Tab focusuje pierwszy sortowalny nagłówek, Enter wyzwala sortowanie
Uruchom testy na istniejącym komponencie. Jeśli jakiekolwiek padają, powiedz mi,czy oczekiwanie testu jest błędne, czy komponent wymaga naprawy.Kiedy TDD z AI działa najlepiej
Dział zatytułowany „Kiedy TDD z AI działa najlepiej”Podejście TDD z AI działa znakomicie dla:
- Czystych funkcji z jasnymi wejściami i wyjściami (parsery, formatery, walidatory, kalkulatory)
- Handlerów endpointów API, gdzie możesz zdefiniować kontrakty request/response jako testy
- Logiki transformacji danych, gdzie przypadki brzegowe są dobrze zdefiniowane
- Napraw błędów, gdzie błąd można wyrazić jako padający test
Działa gorzej dla:
- Layoutu UI i designu wizualnego (testy nie mogą zweryfikować, czy coś “dobrze wygląda”)
- Złożonej logiki integracji, gdzie setup testów to więcej kodu niż implementacja
- Optymalizacji wydajności, gdzie testy poprawności przechodzą, ale kod jest za wolny
Dla słabych stron łącz TDD z innymi technikami: przegląd wizualny dla UI, benchmarki dla wydajności, testy integracyjne z prawdziwymi serwisami dla złożonej orkiestracji.
Kiedy to się psuje
Dział zatytułowany „Kiedy to się psuje”Agent modyfikuje testy, aby przeszły. To najgroźniejszy tryb awarii. Prompt mówi “napraw implementację, nie testy”, ale pod presją (wiele padających testów) AI czasem idzie na skróty. Zawsze diffuj plik testowy po rundzie implementacji. Jeśli testy się zmieniły, odrzuć i uruchom ponownie.
Testy są zbyt sprzężone z detalami implementacji. Jeśli twoje testy sprawdzają, czy konkretna wewnętrzna funkcja została wywołana lub czy powstała określona pośrednia struktura danych, stają się kruche. Testuj interfejs publiczny — wejścia i wyjścia — nie ścieżkę implementacji. Dodaj regułę do .cursor/rules: “Testy powinny assertować tylko na zwracanych wartościach publicznych funkcji i efektach ubocznych, nigdy na wewnętrznych detalach implementacji.”
AI pisze testy, które przechodzą trywialnie. Uważaj na testy typu expect(result).toBeDefined() lub expect(typeof result).toBe('string') — te przechodzą dla praktycznie każdej implementacji. Testy powinny assertować konkretne wartości: expect(result).toBe('<h1>Hello</h1>').
Raport pokrycia mówi 95%, ale bugi nadal się wymykają. Pokrycie linii jest konieczne, ale nie wystarczające. Dodaj wymagania pokrycia gałęzi i testy mutacyjne. Agent może skonfigurować Stryker mutation testing, aby zweryfikować, że twoje testy faktycznie łapią bugi, a nie tylko wykonują ścieżki kodu.
Pętla TDD się zacina. Jeśli Agent nie robi postępu po 3-4 iteracjach (liczba testów zostaje taka sama), pozostałe testy prawdopodobnie ujawniają fundamentalny problem projektowy. Zatrzymaj pętlę, użyj trybu Ask, aby przeanalizować padające testy, i rozważ, czy podejście implementacyjne wymaga zmiany, zamiast łatać pojedyncze awarie testów.