Behavior-driven development z agentami AI
ENBehavior-driven development (BDD) z agentami AI traktuje scenariusze Gherkin, zapisane jako Given/When/Then i zatwierdzone przez człowieka, jako wykonywalny kontrakt, pod który agent implementuje kod. Agent szkicuje scenariusze i kod kroków Cucumbera, człowiek zatwierdza jedno i drugie, a sesja implementująca nie może ich zmienić. O tym, kiedy zachowanie jest gotowe, rozstrzyga przebieg Cucumbera w CI.
Ta strona jest dla programistów i tech leadów, u których product owner albo testerzy już czytają scenariusze, albo którzy utrzymują zestaw testów Cucumbera. Twój koszyk przechodzi wszystkie testy jednostkowe, a i tak trafia na produkcję z błędem: reguła stanów magazynowych żyła w tickecie, którego agent nigdy nie zobaczył, a gdy scenariusz w końcu się wysypał, agent zamienił Then the cart holds 2 na Then the cart holds at least 1. BDD rozwiązuje pierwszy problem tylko wtedy, gdy rozwiążesz też drugi.
Co dostajesz z BDD z agentami
Dział zatytułowany „Co dostajesz z BDD z agentami”- Regułę decyzyjną: kiedy Gherkin zarabia na swoją dodatkową warstwę, a kiedy wystarczą zwykłe testy akceptacyjne.
- Pętlę w pięciu krokach: example mapping, zablokowany plik
.feature, kod kroków czerwony z właściwego powodu, implementacja i porządki w bibliotece kroków. - Cztery prompty do skopiowania dla Claude Code, Codeksa i Cursora oraz blokadę sesji dla każdego narzędzia.
- Job CI, który uruchamia testy, odrzuca zmiany kontraktu i nieużywane kroki oraz publikuje raport HTML jako dowód, który recenzent czyta zamiast diffu.
- Sposoby, w jakie BDD z agentami zawodzi, łącznie z pułapką kodu wyjścia Cucumber.js, i drogę wyjścia z każdego z nich.
Kiedy Gherkin się opłaca zamiast zwykłych testów akceptacyjnych?
Dział zatytułowany „Kiedy Gherkin się opłaca zamiast zwykłych testów akceptacyjnych?”Gherkin dokłada warstwę: plik .feature oraz definicje kroków, które wiążą każde zdanie z kodem. Ta warstwa się zwraca, gdy zachowanie zatwierdzają osoby, które nie czytają kodu. W przeciwnym razie to jeszcze jedno miejsce, w którym agent schowa słabą asercję. Ogólna metoda, czyli jedno kryterium to jeden czerwony test, jest opisana w artykule o wykonywalnych kryteriach akceptacji; ta strona dotyczy wariantu z Gherkinem.
| Sytuacja | Użyj | Dlaczego |
|---|---|---|
| Scenariusze zatwierdza product owner, tester albo ekspert domenowy | Gherkin + Cucumber | Podpisują zdania, a nie kod testów |
| Jedna reguła ma wiele przykładów (ceny, limity, lokalizacje) | Scenario Outline w Gherkinie | Tabela Examples sprawia, że wpisywanie przypadków na sztywno widać od razu, a nowy wiersz kosztuje niewiele |
| Utrzymujesz już testy w Cucumberze, Behacie albo SpecFlow | Gherkin | Agent rozbudowuje istniejącą bibliotekę kroków zamiast zaczynać drugi styl |
| Testy czytają tylko programiści | Zwykłe testy Playwright albo API | Warstwa kroków kosztuje więcej, niż daje |
| Zachowanie nie ma granicy widocznej dla użytkownika | Testy jednostkowe albo testy właściwości | Scenariusz przywiązałby się do wnętrza kodu |
W projektach JavaScript wybierz jeden z dwóch runnerów. Cucumber.js (@cucumber/cucumber 13.2.1 w npm, sprawdzone 2026-09-26) uruchamia pliki .feature bezpośrednio i steruje Playwrightem jako biblioteką. playwright-bdd (9.2.1, peer @playwright/test 1.44 lub nowszy) zamienia pliki .feature w pliki Playwright Test poleceniem npx bddgen, więc zachowujesz fixtures, trace’y i sharding Playwrighta. Przykład poniżej używa Cucumber.js; pętla i prompty są takie same w obu przypadkach.
Jak działa pętla BDD z agentem?
Dział zatytułowany „Jak działa pętla BDD z agentem?”Pętla składa się z dwóch pull requestów. Pierwszy niesie kontrakt: plik .feature i czerwony kod kroków. Zatwierdza go człowiek. Drugi niesie implementację z sesji, która nie może dotknąć pierwszego. Pull request z kontraktem jest czerwony z założenia, więc nigdy nie jest scalany samodzielnie: otwórz pull request z implementacją z gałęzią kontraktu jako bazą, scal go do tej gałęzi, gdy CI będzie zielone, a potem scal do main pull request z kontraktem, który jest już zielony. W main nigdy nie ląduje czerwony scenariusz, a strażnik ścieżek nadal porównuje implementację z zatwierdzonym kontraktem.
- Zmapuj przykłady z agentem. Agent zamienia historyjkę w reguły, konkretne przykłady i otwarte pytania. Ty i product owner odpowiadacie na pytania i zatwierdzacie scenariusze.
- Dodaj plik
.featuredo pull requesta z kontraktem. Od tej chwili scenariusze (features/**/*.feature) należą do product ownera, a kod kroków (features/steps/,features/support/) do tech leada. - Wygeneruj kod kroków, który pada z właściwego powodu. Osobna sesja pisze definicje kroków i uruchamia zestaw testów. Każdy scenariusz musi padać na brakującym zachowaniu, a nie na brakującym selektorze czy rzuconym
TypeError. - Implementuj do zielonego w świeżej sesji, która ładuje blokadę, we własnym worktree, na gałęzi opartej na gałęzi kontraktu.
- Skonsoliduj bibliotekę kroków po wydaniu funkcji, żeby kolejna historyjka używała istniejących kroków zamiast dopisywać niemal identyczne.
Krok 1: zmapuj przykłady, zanim napiszesz Gherkin
Dział zatytułowany „Krok 1: zmapuj przykłady, zanim napiszesz Gherkin”Example mapping dzieli historyjkę na reguły, jeden lub więcej przykładów na regułę oraz pytania, na które nikt jeszcze nie zna odpowiedzi. Pierwsze przejście zleć agentowi. Czyta ticket i kod szybciej, niż zbierze się spotkanie, a jego pytania odsłaniają luki.
Szkic to propozycja. Product owner odpowiada na pytania, usuwa tagi @question i zatwierdza plik. Oto zatwierdzony wynik dla koszyka ograniczonego stanem magazynowym:
Feature: Stock-limited cart Shoppers cannot put more units in the cart than the warehouse holds.
Rule: The cart never holds more units than are in stock
Scenario Outline: Adding up to the stock limit Given "<product>" has <stock> units in stock When the shopper adds <qty> "<product>" to the cart Then the cart holds <qty> "<product>"
Examples: | product | stock | qty | | Super Widget | 2 | 2 | | Gear Kit | 5 | 1 |
Scenario: Blocked from exceeding the stock limit Given "Super Widget" has 2 units in stock And the shopper already has 2 "Super Widget" in the cart When the shopper tries to add 1 more "Super Widget" Then the shopper sees "Only 2 in stock" And the cart holds 2 "Super Widget"Drugi wiersz przykładów ma znaczenie. Przy jednym przykładzie agent może przejść test, sprawdzając "Super Widget" i 2. Przy dwóch musi zaimplementować regułę. Zwróć uwagę na brzmienie kroku przygotowującego w drugim scenariuszu. Cucumber dopasowuje krok do definicji bez względu na słowo kluczowe Given/When/Then, więc krok przygotowujący stan i asercja potrzebują różnych zdań: the shopper already has 2 … in the cart ustawia stan, a the cart holds 2 … zawsze tylko sprawdza. Gdyby oba używały tego samego zdania, warunek wstępny albo sprawdzałby pusty koszyk i padał, albo Then stałby się krokiem przygotowującym, który niczego nie sprawdza. Jeśli product owner pisze po polsku, Gherkin to obsługuje: # language: pl w pierwszej linii przełącza słowa kluczowe na Funkcja, Scenariusz, Zakładając, Gdy, Wtedy (pełną listę pokazuje npx cucumber-js --i18n-keywords pl).
Krok 2: zablokuj plik .feature
Dział zatytułowany „Krok 2: zablokuj plik .feature”Dodaj plik .feature do pull requesta z kontraktem, który może zaakceptować tylko właściciel kodu (code owner), oznacz go etykietą bdd-contract i dodaj strażnika CI pokazanego w sekcji Jak udowodnić zachowanie bez czytania diffu?. Product owner akceptuje kontrakt w tym pull requeście, ale pull request pozostaje otwarty: testy są czerwone, dopóki nie trafi do niego implementacja. Pull request z implementacją celuje w gałąź kontraktu, a nie w main, i oba są scalane, gdy testy przejdą. Pięć warstw ochrony, łącznie z przykładami holdout trzymanymi poza repozytorium, opisuje artykuł o ochronie wyroczni.
Krok 3: wygeneruj kod kroków czerwony z właściwego powodu
Dział zatytułowany „Krok 3: wygeneruj kod kroków czerwony z właściwego powodu”Skonfiguruj Cucumber.js raz. Ten cucumber.json ładuje pliki kroków w TypeScripcie i zapisuje raport HTML:
{ "default": { "paths": ["features/**/*.feature"], "import": ["features/steps/**/*.ts", "features/support/**/*.ts"], "format": ["progress", "html:reports/cucumber.html"] }}{ "scripts": { "test:bdd": "NODE_OPTIONS='--import tsx' cucumber-js" }}Ładuj tsx przez NODE_OPTIONS, a nie przez opcję loader Cucumbera: z tsx 4.23 na Node 22 wpis "loader": ["tsx"] kończy się błędem „tsx must be loaded with —import instead of —loader” (sprawdzone 2026-09-26 z @cucumber/cucumber 13.2.1). Zapis z NODE_OPTIONS to składnia powłoki POSIX; na Windowsie użyj cross-env.
Uruchom npm run test:bdd -- --dry-run, żeby zobaczyć szkielety kodu, które Cucumber proponuje dla niezdefiniowanych kroków, a potem przekaż zadanie sesji, która może pisać wyłącznie w features/steps/ i features/support/. W Claude Code zablokuj scenariusze, ale nie kod kroków:
claude --disallowedTools "Edit(/features/**/*.feature)"Codex 0.157.1 i Cursor nie mają zweryfikowanej blokady zapisu dla ścieżki, więc na tym etapie jedyną kontrolą nienaruszalności scenariuszy jest recenzja pull requesta z kontraktem przez właściciela kodu.
Najpierw uruchom aplikację (npm start w drugim terminalu): test:bdd steruje aplikacją na porcie 3000, a bez niej każdy scenariusz pada z odmową połączenia, czyli z niewłaściwego powodu.
Czytaj czerwony przebieg, a nie kod kroków linijka po linijce. Każda porażka powinna nazywać brakujące zachowanie, na przykład oczekiwany tekst Only 2 in stock, który się nie pojawia. Dodaj kod kroków w commicie razem z plikiem .feature w tym samym pull requeście z kontraktem.
Krok 4: implementuj, aż testy będą zielone
Dział zatytułowany „Krok 4: implementuj, aż testy będą zielone”Sesja implementująca dostaje kontrakt i nic innego do zmiany. Daj jej gałąź opartą na gałęzi kontraktu (tu cart-stock-contract) we własnym worktree, żeby plik .feature i kod kroków były już w kopii roboczej. Wbudowana flaga worktree narzędzia (claude --worktree, codex exec --worktree) startuje z domyślnej bazy narzędzia, a nie z gałęzi kontraktu, więc utwórz worktree samodzielnie:
git worktree add ../cart-stock-impl -b cart-stock-impl cart-stock-contractcd ../cart-stock-implls features/cart/stock-limit.feature # the contract must be hereUtrzymuj aplikację uruchomioną na porcie 3000 z tego worktree (npm start), tak jak w kroku 3. Sposób blokady sesji zależy od narzędzia.
Przekaż blokadę jako flagę wiersza poleceń tylko sesjom implementującym, żeby nie blokowała sesji piszącej kod kroków w kroku 3:
# Terminal, inside ../cart-stock-impl: interactive implementationclaude --disallowedTools "Edit(/features/**)"
# Terminal or CI: headless run with a spending capclaude -p "Implement features/cart/stock-limit.feature until npm run test:bdd passes. Do not edit features/." \ --disallowedTools "Edit(/features/**)" \ --permission-mode acceptEdits \ --allowedTools "Bash(npm run test:bdd)" "Bash(npm run test:bdd *)" \ --max-budget-usd 5Reguły Edit obejmują własne narzędzia plikowe Claude Code i polecenia plikowe, które rozpoznaje w Bashu, ale nie skrypt Node zapisujący pliki, więc wiążącą kontrolą pozostaje strażnik CI. Przed pierwszym prawdziwym przebiegiem poproś sesję interaktywną, żeby dopisała linię do features/cart/stock-limit.feature: edycja musi zostać odrzucona. Flagi sprawdzone na Claude Code 2.1.283 w dniu 2026-09-26; składnię reguł wyjaśnia artykuł o uprawnieniach i sandboksach.
Dodaj regułę kontraktu do AGENTS.md, który Codex czyta przed rozpoczęciem pracy w zaufanym projekcie:
## BDD contract- Never edit files under features/. The product owner approves them.- If a scenario looks wrong, stop and explain why in your final message.# Terminal (Codex CLI 0.157.1), inside ../cart-stock-implcodex exec --approve-for-me -o bdd-report.md \ "Implement features/cart/stock-limit.feature until npm run test:bdd passes. Do not edit features/."--approve-for-me kieruje prośby o zgodę do automatycznej recenzji w sandboksie workspace-write. Ta strona nie pokazuje blokady zapisu dla konkretnej ścieżki w Codeksie, bo w wersji 0.157.1 żadnej nie zweryfikowano; kontraktu pilnuje strażnik CI. Sandbox workspace-write nie ma dostępu do sieci, dopóki sandbox_workspace_write.network_access nie jest ustawione na true (klucz obecny w 0.157.1), więc przebieg Playwrighta na localhost:3000 prosi o eskalację, o której decyduje automatyczna recenzja; jeśli ją odrzuci, każdy scenariusz padnie z błędem połączenia, czyli z niewłaściwego powodu, a /approve ponawia jedną odmowę.
Zacznij w Plan Mode, który według dokumentacji Cursora tworzy szczegółowy plan implementacji, zanim napisze jakikolwiek kod, i sprawdź, czy plan uruchamia npm run test:bdd i zostawia features/ w spokoju. Obie reguły kontraktu z zakładki Codex umieść w regule projektu (Rules), żeby widziała je każda sesja agenta.
Długą pętlę od czerwonego do zielonego przekaż Cloud Agentowi i przejrzyj pull request, który otworzy; strażnik CI działa także tam. Kontraktu pilnuj w CI, a nie lokalnym hookiem Cursora (dokumentacja Cursora sprawdzona 2026-08-28).
Jeśli sesja pisząca kod kroków ma najpierw obejrzeć działającą stronę, zanim wybierze lokatory, podłącz serwer Playwright MCP (@playwright/mcp). Konfigurację dla wszystkich trzech narzędzi opisuje artykuł o serwerach MCP do automatyzacji przeglądarki.
Krok 5: utrzymuj małą bibliotekę kroków
Dział zatytułowany „Krok 5: utrzymuj małą bibliotekę kroków”Agenci piszą nowy krok dla każdego sformułowania: „adds 2 to the cart”, „puts 2 in the cart”, „adds two items”. Formatter usage Cucumbera wypisuje każdą definicję kroku razem ze scenariuszami, które jej używają, a nieużywane definicje oznacza jako UNUSED.
Jak udowodnić zachowanie bez czytania diffu?
Dział zatytułowany „Jak udowodnić zachowanie bez czytania diffu?”Recenzent czyta trzy rzeczy: zatwierdzony plik .feature, raport HTML Cucumbera i wynik strażnika. Ten job GitHub Actions dostarcza wszystkie trzy przy każdym pull requeście:
name: bddon: pull_requestpermissions: contents: readjobs: bdd: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: fetch-depth: 0 persist-credentials: false - uses: actions/setup-node@v7 with: node-version: 22 - run: npm ci - run: npx playwright install --with-deps chromium - name: Implementation PRs must not change the contract if: ${{ !contains(github.event.pull_request.labels.*.name, 'bdd-contract') }} env: BASE_REF: ${{ github.base_ref }} run: | changed=$(git diff --name-only "origin/$BASE_REF"...HEAD -- features) if [ -n "$changed" ]; then echo "::error::This PR changes the BDD contract:"; echo "$changed"; exit 1 fi - name: No unused step definitions run: | if npm run test:bdd -- --dry-run --format usage | grep -q UNUSED; then echo "::error::Unused step definitions. Consolidate them."; exit 1 fi - run: npm run build - run: npx start-server-and-test start http://localhost:3000 test:bdd - uses: actions/upload-artifact@v7 if: always() with: name: cucumber-report path: reports/Job zakłada skrypt start, który serwuje zbudowaną aplikację na porcie 3000; dodaj start-server-and-test do devDependencies, żeby npx nie pobierał go przy każdym uruchomieniu.
W pull requeście z kontraktem ostatni krok npm run test:bdd pada z założenia; ten czerwony przebieg jest dowodem, że kod kroków pada z właściwego powodu. Pull requesta nie scala się, dopóki jest czerwony: najpierw do gałęzi kontraktu trafia oparty na niej pull request z implementacją, job przechodzi ponownie na zielono i dopiero wtedy kontrakt trafia do main. Ustaw job bdd jako wymagany status check dla main, żeby czerwonego kontraktu nie dało się scalić przez pomyłkę. Chroń także gałąź kontraktu, żeby kod trafiał do niej wyłącznie przez pilnowany przez strażnika pull request z implementacją; jeśli nieaktualne akceptacje są odrzucane (Dismiss stale approvals), product owner ponownie akceptuje zielony już pull request z kontraktem przed scaleniem.
Rozdziel własność w .github/CODEOWNERS wpisami /features/**/*.feature @your-org/product-owners i /features/steps/ @your-org/tech-leads oraz /features/support/ @your-org/tech-leads i /.github/ @your-org/tech-leads (jeden wzorzec na linię), a potem włącz Require review from Code Owners. Pull request z etykietą bdd-contract wymaga wtedy akceptacji product ownera dla brzmienia scenariuszy i tech leada dla kodu kroków, więc osłabionej asercji nie zatwierdza nikt, kto nie czyta kodu. Job nie ma dostępu do sekretów i używa tokenu tylko do odczytu, więc można go bezpiecznie uruchamiać na pull requestach z gałęzi. Sam w sobie nie jest jednak odporny na obejście: przy wyzwalaczu pull_request GitHub uruchamia bdd.yml z gałęzi pull requesta, więc pull request z implementacją może usunąć strażnika ścieżek i przejść, a każdy, kto może dodać etykietę bdd-contract, może sprawić, że strażnik zostanie pominięty. Dlatego w CODEOWNERS jest też /.github/: przy wymaganym przeglądzie Code Owners ani zmieniony workflow, ani zmiana w features/ nie zostanie scalona bez właściciela.
| Co jest sprawdzane | Jak | Kto zatwierdza |
|---|---|---|
| Scenariusze opisują właściwe zachowanie | Pull request z kontraktem i plikiem .feature | Product owner |
| Kod kroków pada z właściwego powodu | Czerwony przebieg wklejony do pull requesta z kontraktem | Programista albo tech lead |
| Kod kroków jest wystarczająco mocny | Prompt audytowy z artykułu o wykonywalnych kryteriach akceptacji, wynik mutacyjny z artykułu o sile wyroczni | Tech lead, raz na zestaw testów |
| Implementacja spełnia kontrakt | npm run test:bdd zielony w CI, raport HTML w artefaktach | CI |
| Kontrakt nie przesunął się pod kod | Strażnik ścieżek i CODEOWNERS | CI, a do tego product owner dla scenariuszy i tech lead dla kodu kroków |
Bezpieczeństwo, płatności i migracje nadal przechodzą ludzki code review. Scenariusze zmieniają domyślny tryb dla całej reszty; tę zmianę opisuje artykuł o czytaniu dowodów zamiast kodu.
Co się psuje w BDD z agentami?
Dział zatytułowany „Co się psuje w BDD z agentami?”Agent edytuje scenariusz albo zmienia jego tagi. Dopisuje @wip lub @skip do czerwonego scenariusza albo przeredagowuje Then. Sygnał: strażnik ścieżek pada. Wyjście: odrzuć pull request, zacznij od nowa w świeżej sesji z załadowaną blokadą i powtórz w prompcie zakaz zmiany tagów. Jeśli zdarzy się to dwa razy na jednym scenariuszu, prawdopodobnie scenariusz jest błędny: wróć z nim do product ownera.
Kod kroków słabnie, a plik .feature zostaje bez zmian. Agent zamienia toHaveText('Only 2 in stock') na toBeVisible() w cart.steps.ts. Sygnał: strażnik ścieżek, bo kod kroków leży w features/. Wyjście: trzymaj definicje kroków w zablokowanej ścieżce; zmiana kodu kroków przechodzi przez pull request z etykietą bdd-contract, który CODEOWNERS kieruje do tech leada, a nie do product ownera.
--dry-run zgłasza niezdefiniowane kroki i mimo to kończy się kodem 0. W @cucumber/cucumber 13.2.1 (sprawdzone 2026-09-26) cucumber-js --dry-run zwraca 0 nawet przy niezdefiniowanych krokach, z --strict i bez. Pełny przebieg kończy się kodem 1 przy krokach niezdefiniowanych albo pending. Wyjście: bramką niech będzie pełny przebieg, a dry run służy tylko do szkieletów kodu i raportu usage.
Scenariusze wyglądają jak skrypty klikania. „When I click #add-btn” psuje się przy każdym redesignie i kusi agenta, żeby edytował plik .feature. Wyjście: przepisz kroki na język klienta („adds 2 to the cart”), a selektory trzymaj w kodzie kroków; prompt do example mappingu właśnie dlatego wymaga kroków deklaratywnych.
Implementacja wpisuje przykłady na sztywno. Kod sprawdza "Super Widget". Sygnał: nowy wiersz Examples pada albo recenzent widzi w kodzie aplikacji literały z tabeli. Wyjście: dodaj wiersze przez pull request z kontraktem, a jeden zestaw wierszy trzymaj jako holdout uruchamiany tylko w CI, jak opisuje artykuł o ochronie wyroczni.
Kroki ścigają się z UI i w CI bywają niestabilne (flaky). Sygnał: ludzie restartują job, aż będzie zielony. Wyjście: zastąp page.waitForTimeout asercjami web-first, ustawiaj stan przez testowe API zamiast przez UI i napraw niestabilny test, zanim dodasz kolejne scenariusze. Jeśli niestabilność nie znika, przenieś testy na playwright-bdd, żeby korzystać z trace’ów, fixture’ów i ponowień pojedynczych testów w Playwrighcie (Cucumber.js ma --retry, ale nie ma przeglądarki trace’ów).
Dokąd dalej z BDD
Dział zatytułowany „Dokąd dalej z BDD”- Zacznij od ogólnej metody, czyli jedno kryterium to jeden czerwony test: wykonywalne kryteria akceptacji.
- Utrzymuj specyfikację funkcji, z której biorą się scenariusze, jako źródło prawdy: spec-driven development.
- Steruj Playwrightem przez agenta dokładniej: testy end-to-end.
- Połącz scenariusze na poziomie zachowania z pętlą czerwone/zielone na poziomie jednostek: TDD z AI.
- Nazywaj pojęcia domenowe w krokach spójnie: domain-driven design z AI.