Przejdź do treści

TDD red-green z agentami AI: test musi najpierw nie przejść

Dla CTO i programistówHarness i kontrole

Filip PapranecZałożyciel Digitaliko

Test, który przechodzi na starym kodzie, niczego nie dowodzi. Ilustracja: ten sam test uruchomiony dwa razy, czerwony na gałęzi bazowej i zielony na gałęzi z funkcją.Test, który przechodzi na starym kodzie, niczego nie dowodzi. Ilustracja: ten sam test uruchomiony dwa razy, czerwony na gałęzi bazowej i zielony na gałęzi z funkcją.

Agenci AI piszą testy, które przechodzą. To nie to samo co testy, które czegoś dowodzą, a senior, który musi je od siebie odróżniać w review, traci czas, który AI miało zaoszczędzić. Dlatego nasza bramka uruchamia każdy nowy test najpierw na starym kodzie. Jeśli przejdzie i tam, PR jest czerwony.

To red-green-refactor wymuszany przez maszynę zamiast dyscypliny. U nas większość kodu pisze AI. Każdą zmianę sprawdza senior i odpowiada za produkcję. Red-proof sprawia, że ten senior może ufać zielonemu checkowi.

Dlaczego testy pisane przez AI są ryzykiem?

Proszę poprosić agenta, żeby „dodał testy”, a dostaną Państwo testy, które robią się zielone. Bez wyraźnych wytycznych pisze je po kodzie i pod kod, więc potwierdzają to, co kod już robi. Niektóre w ogóle nie wywołują kodu. Widzieliśmy testy, które czytały plik źródłowy jako tekst i szukały nazwy funkcji. Przechodzą na każdej wersji, która zawiera to słowo.

Pokrycie rośnie. Dowód zostaje tam, gdzie był. Recenzent staje się ostatnią linią obrony, a dokładne czytanie każdego testu to właśnie praca, której mieli Państwo przestać wykonywać.

Gdzie jest Państwa zespół?

Czym jest red-proof?

Czerwonym krokiem red-green-refactor, sprawdzanym przez skrypt:

  1. Czerwony. Nowy test musi nie przejść na gałęzi bazowej, czyli na kodzie sprzed zmiany.
  2. Zielony. Ten sam test musi przejść na gałęzi.
  3. Bez testu nie ma zmiany. Zmieniony kod bez zmienionego testu to porażka.
Red-proof na przykładowym teście „rabat działa tylko raz”: agent commituje sam test, na gałęzi bazowej main test nie przechodzi (FAIL), na gałęzi ze zmianą przechodzi (PASS). Gdyby przechodził też na main, PR jest czerwony.Red-proof na przykładowym teście „rabat działa tylko raz”: agent commituje sam test, na gałęzi bazowej main test nie przechodzi (FAIL), na gałęzi ze zmianą przechodzi (PASS). Gdyby przechodził też na main, PR jest czerwony.

Brief, który nasz agent dostaje do zadań programistycznych, mówi to samo słowami. Napisz jeden test dla najbardziej ryzykownego kryterium i zacommituj go osobno. Musi nie przechodzić na niezmienionym kodzie. Potem implementuj. Nie edytuj testu po jego commicie.

Słowa to prośba. Bramka to zamek.

Jak sprawdza to bramka?

To zwykły bash, gate.sh w środowisku naszego agenta. Rdzeń, skrócony:

# red-proof: the branch's new/changed tests must FAIL on the base code, or they prove nothing
changed=$(git diff --name-only "$base"...HEAD; git ls-files --others --exclude-standard)
tests=$(printf '%s\n' "$changed" | grep -E '(^|/)(tests?|__tests__|spec)/|\.(test|spec)\.[a-z]+$')
src=$(printf '%s\n' "$changed" | grep -E '\.(ts|tsx|js|jsx|mjs|py|go|rs|php|rb)$' \
      | grep -vE '(^|/)(tests?|__tests__|spec)/|\.(test|spec)\.')
if [ -z "$tests" ]; then
    [ -n "$src" ] && echo "FAIL red-proof: source changed but no test was added or changed"
else
    cmd=$(jq -r .scripts.test package.json)
    git worktree add -q --detach "$W/base" "$base"      # the old code, next to the branch
    for t in $tests; do cp "$t" "$W/base/$t"; done      # the new tests, onto the old code
    if (cd "$W/base" && sh -c "$cmd") > "$W/red-test.log" 2>&1; then
        echo "FAIL red-proof: the new tests also pass on $base, so they do not prove the change"
    else
        echo "PASS red-proof: new tests fail on $base and pass on the branch"
    fi
fi

Wokół niego są trzy zasady:

  • Testy bez skryptu test nie przechodzą. Nic by ich nie uruchomiło.
  • Recenzent czyta, dlaczego testy nie przeszły na bazie. Bramka wypisuje pierwsze asercje z czerwonego przebiegu. Błąd z powodu brakującego pliku lub importu to nie red-proof i recenzent to wskazuje.
  • Testy czytające kod jako tekst wracają do poprawki. Instrukcje recenzenta je wymieniają: test musi wywołać funkcję, wyrenderować komponent albo odpytać ścieżkę.

Bramka nie oznacza, że więcej testów to lepiej. Bezsensowne testy usuwamy. Czasem rozwiązaniem jest uproszczenie, a nie dodawanie.

Co się zepsuło: fałszywe zaliczenie przy czerwonym main

Pierwsza wersja miała dziurę. Gdy gałąź bazowa już nie przechodziła własnego zestawu testów, każdy nowy test wyglądał tam na czerwony. Bramka zgłaszała PASS dla testów, które niczego nie dowodziły.

Poprawka: jeśli zestaw na bazie już nie przechodzi, uruchamiamy osobno tylko nowe pliki testów. Na bazie muszą nie przejść, na gałęzi muszą przejść. Jeśli nie dają się uruchomić osobno, wynik jest nierozstrzygnięty, a nierozstrzygnięty oznacza porażkę.

scoped=""
(cd "$W/base" && sh -c "$cmd") > "$W/base-test.log" 2>&1 || scoped=" $(echo $tests)"

Druga dziura to środowisko. W jednym z naszych projektów red-proof uruchamiał testy bez właściwej konfiguracji: brakowało narzędzia (turbo: command not found) i działała inna wersja Node. Testy na bazie padały, zanim w ogóle wystartowały. Bramka brała to za red-proof, więc każdy red-proof był tam fałszywym zaliczeniem.

Ta sama poprawka zaostrzyła zrzuty ekranu. Widoczna zmiana interfejsu przechodziła wcześniej z dowolnym załączonym PNG. Teraz potrzebuje zrzutu przed i po.

Czy każda zmiana potrzebuje czerwonego testu?

Nie. Czysty refactor lub zmiana konfiguracji nie mają nic nowego do udowodnienia. Wszystko inne przechodzi przez bramkę:

ZmianaRed-proofKto decyduje
Nowa funkcja lub poprawka błęduTak. Nowy test nie przechodzi na gałęzi bazowej i przechodzi na gałęzi.Bramka
Zmiana kodu bez testuPorażka, PR jest czerwony.Bramka
Czysty refactor lub konfiguracjaMoże być zwolniona: etykieta skip-red-proof w naszym publicznym starterze.Człowiek, nigdy agent
Widoczna zmiana interfejsuZrzut przed i po, oprócz testu.Bramka i recenzent

Odwrotnie jest z krytycznym oprogramowaniem, które liczy ceny albo decyduje o ludziach. Tam bramki nigdy nie wyłączamy.

Jest różnica między aplikacją sklejoną na szybko vibe codingiem a czymś krytycznym. Nawet jeśli kod pisze agent, właśnie tam potrzebujesz testów i wszystkich bramek.

Filip Papranec
Filip Papranec
Założyciel Digitaliko

Jak dodać red-proof do własnego CI?

Ta sama bramka jest w naszym publicznym Claude Code production starter na licencji MIT. Proszę uruchomić ją lokalnie, zanim uznają Państwo pracę za skończoną:

bash scripts/gate.sh all origin/main

Kolejność, którą polecamy:

  1. Najpierw test

    Agent pisze jeden test dla najbardziej ryzykownego kryterium i commituje go osobno.

  2. Bramka lokalnie

    Zanim uzna pracę za skończoną, uruchamia gate.sh względem origin/main.

  3. Job w CI

    Osobny job red-proof na każdym pull requeście.

  4. Wyjątki przyznaje człowiek

    Etykieta skip-red-proof tylko dla czystego refactoru lub konfiguracji.

Starter zawiera prostszą wersję bez osobnego ponownego uruchomienia, więc proszę utrzymywać główną gałąź na zielono. To i tak dobra rada.

Zasady takie jak „test musi najpierw nie przejść” należą do repozytorium, nie do głowy seniora. Więcej w CLAUDE.md i AGENTS.md: zasady dla kodu AI w zespole. Dlaczego sprawdzenia wokół modelu są ważniejsze niż sam model, wyjaśniamy w Harness engineering w praktyce: od issue do PR.

Chcą Państwo testów od AI, które czegoś dowodzą?

Takie bramki konfigurujemy bezpośrednio w repozytorium zespołu w ramach AI-native SDLC dla zespołów deweloperskich. Zespołowi brakuje raczej rąk niż zasad? Proszę zobaczyć senior developera dla Państwa zespołu. Od 2023 roku dostarczyliśmy ponad 15 projektów produkcyjnych. Senior dołącza do Państwa repozytorium i pull requestów pierwszego dnia.

Odezwiemy się w ciągu dwóch dni roboczych z kilkoma pytaniami o Państwa pracę. Potem ustalimy, co zrobimy najpierw.

Jeśli wolą Państwo najpierw porozmawiać, proszę zarezerwować 15-minutową rozmowę wstępną.

Które z Państwa testów wciąż by przechodziły, gdyby usunąć funkcję?

W skrócie

  • Test, który przechodzi przed zmianą i po niej, nie dowodzi zmiany.
  • Red-proof: nowy test musi nie przejść na gałęzi bazowej i przejść na gałęzi.
  • Sprawdza to skrypt w CI, a nie dyscyplina. Wyjątki przyznaje tylko człowiek.
  • Gdy main już nie przechodzi, nowe pliki testów uruchamia się osobno. Nierozstrzygnięty wynik to porażka.

Najczęstsze pytania

Czym jest red-green-refactor?

Cyklem programowania sterowanego testami: napisać test, który nie przechodzi (czerwony), napisać kod, dzięki któremu przejdzie (zielony), a potem posprzątać (refactor). Czerwony krok dowodzi, że test sprawdza nowe zachowanie.

Jak stosować TDD z agentami AI?

Agent najpierw pisze jeden test i commituje go osobno, a dopiero potem implementuje. Proszę wymusić to w CI: nowy test musi nie przejść na gałęzi bazowej i przejść na gałęzi. W przeciwnym razie PR jest czerwony.

Dlaczego nowy test musi nie przejść na gałęzi bazowej?

Test, który przechodzi przed zmianą i po niej, nie testuje zmiany. Może nie testować niczego albo tylko zachowanie, które już istniało.

Co, jeśli główna gałąź ma już nieprzechodzące testy?

Wtedy cały zestaw nie przechodzi na gałęzi bazowej i każdy nowy test wygląda na czerwony. Proszę uruchomić osobno tylko nowe pliki testów. Jeśli nie dają się uruchomić osobno, wynik trzeba traktować jako porażkę, nie zaliczenie.

Czy agent może pominąć red-proof?

Nie sam. Czysty refactor lub zmiana konfiguracji mogą być zwolnione, ale decyduje o tym człowiek, etykietą na pull requeście.

Zapytaj AI o ten artykuł

AI przy odpowiedzi wykorzysta też nasze inne artykuły i doświadczenia z prawdziwych projektów.

ChatGPTClaudePerplexity
Engineering

Kolejne artykuły

Harness i kontrole

Context engineering dla agentów kodujących: co trzymać w repo

Context engineering decyduje, co widzi agent AI piszący kod w trakcie pracy. Co trafia do AGENTS.md lub CLAUDE.md, które reguły ładują się tylko tam, gdzie obowiązują, jak zadanie staje się kontekstem i jak utrzymać całość w małym rozmiarze.

Harness i kontrole

Playwright MCP: niech agent AI udowodni, że to działa

Playwright MCP daje agentowi AI prawdziwą przeglądarkę. Jak go używamy, żeby agent sprawdził własną zmianę w działającej aplikacji i oddał PASS lub FAIL ze zrzutami ekranu, co wychwytuje, a czego nie widzą testy jednostkowe, i kiedy lepszym narzędziem są zwykłe skrypty Playwright.

Harness i kontrole

Code review z AI: Claude Code w CI i czego nie widzi

Jak uruchamiamy code review z Claude Code przy każdym pull requeście jedną wielokrotnego użytku GitHub Action: czego szuka recenzent AI, jak świecił na zielono i milczał, czego nie wyłapuje i dlaczego zatwierdza tylko człowiek.