Specification by Example
Domain-language Gherkin.
The scenario that survives a redesign talks about what the system does, not what the page looks like. Domain language survives the next CSS commit. Click-coordinates do not.
Opinion
I've sat in too many sprint reviews where the demo passed acceptance and shipped a bug. The scenario said the user can register; the implementation registered the user and then dropped the welcome email without a word. Acceptance was met by a paraphrase. The fix is the same one Adzic and North have been arguing for twenty years: write the example, in the domain's words, in a form a machine can run.1 If the example doesn't cover the welcome email, the example was wrong. The conversation has to happen before the code, not after.
Most teams get the declarative-versus-imperative line wrong. When I submit the form is what the user does; When I click the third button is what the front-end happens to be wired to today. The first survives a redesign because it talks about behaviour. The second breaks the morning a designer changes the layout. Cucumber's “Writing Better Gherkin” guide makes the rule explicit2 and it is the rule almost every legacy step file ignores. Same shape as F2 Intention-Revealing Names: the domain noun wins over the implementation noun every time.
The pipeline that makes the scenario load-bearing is what stops it being theatre. P2 Spec-First Execution commands writing the AC up front. AI4 Verifiable Specs commands writing it as something a machine can grade. TS2 commands writing it declaratively in domain words. TS1 takes the resulting check and turns it into the documentation. Four tenets and four checkpoints on one artefact. Skip any one and the spec rots into prose nobody runs.
Copy a note and link
Grab this short comment and drop it into a PR comment or an LLM chat to prompt the right change.
Write the scenario in domain language. `When I submit the form` is the spec; `When I click the third button` is the implementation leaking through. Declarative not imperative. The scenario survives a redesign because it talks about what the system does, not what the DOM looks like. /tenet/specification-by-example/TS2
AI eyes only
Rule: write Gherkin in domain language. No DOM selectors, no implementation detail.
Reject: When I click "#submit-btn". Reject: scenarios that use technical IDs the product owner does not recognise. Reject: imperative click-by-click tutorials posing as scenarios.
Generate: declarative scenarios in the ubiquitous language ( When I submit my registration). Step definitions handle the mechanics. The .feature file stays domain-only.
Diagnostic: a stakeholder reads the feature file and recognises every term. If a developer has to translate, the term does not belong in the scenario.
Why?
- Stakeholder, developer and tester share one vocabulary, Evans's ubiquitous language by another name. The scenario is the artefact all three sign.
- The AC in the ticket is the scenario in the feature file. The story moves from analysis to development without anybody rewriting the requirement in code-flavoured prose.
- Declarative steps survive a redesign. When I submit the form still passes after the button moves; When I click the third button breaks the morning the layout changes.
- Slots into the four-tenet spec pipeline, P2 Spec-First Execution → AI4 Verifiable Specs → TS2 → TS1 Living Documentation. One artefact, four checkpoints, each owned by the pillar that polices it.
- Coding agents treat the Gherkin as the canonical spec. A declarative scenario in domain words is a target the model can satisfy. An imperative one is a UI script the model reproduces one click at a time.
- One scenario, one behaviour. The
and-counting rule keeps the document diff-friendly, the failure modes attributable and the regression net legible. - A declarative scenario is parameterisation-ready. Pair with TS3 Parameterised Scenarios when the example becomes a table, with property-based tests when the table becomes infinite.
Origins
Specification by Example crystallised in 2009-2011. Gojko Adzic's Bridging the Communication Gap (2009)6 set the practice; Specification by Example (2011)1 codified it as seven process patterns. Adzic chose the name deliberately over “BDD” or “ATDD”7: he wanted the term with the least negative baggage, and one that did not reduce the practice to a testing activity.
The lineage runs back through Dan North's 2006 introduction of BDD,8 where Given/When/Then was already proposed as a documentation grammar, and further back to Eric Evans's Domain-Driven Design (2003), whose ubiquitous language is the source of the “domain language” clause in the TS2 punch.9 Cucumber's “BDD History” page traces the chain explicitly.10 Liz Keogh's “What is BDD?”11 is the cleanest modern compression: examples in conversation to illustrate behaviour.
The declarative-not-imperative rule, often the part working developers most need, is Cucumber's own Writing Better Gherkin guide.2 The Three Amigos pattern (Dinwiddie, 2009)12 names the conversation half: business, development, testing in one room before the code starts. Wynne and Hellesøy's Cucumber Book13 is the reference text on putting the practice into a working test runner.
In the Prickles canon, TS2 is the third link in a four-tenet pipeline. P2 Spec-First Execution commands writing the AC up front. AI4 Verifiable Specs commands writing it as something a machine can grade. TS2 commands writing it declaratively in domain words. TS1 takes the resulting check and turns it into the documentation. The chain is the operating principle and TS2 is its testing-side phrasing rule.
Quotes
Your scenarios should describe the intended behaviour of the system, not the implementation.
Using examples in conversation to illustrate behaviour.
Given some initial context (the givens), When an event occurs, Then ensure some outcomes.
Specification by Example is a set of process patterns that facilitate change in software products to ensure the right product is delivered effectively.
Evidence
17 external sources, ranked by author authority. The first five are the canon; expand to see the rest, including the qualifiers and the named opposers. Each links out to its primary source.
- 01Specification by ExampleSupportsCodifies the practice as seven process patterns. The canonical reference. Adzic chose the term over BDD or ATDD for the cleanest description of the artefact: a specification, illustrated by examples, that the team agreed on.
- 02The earlier book that established the practice. Acceptance tests as agreed truth between business and developers; SBE’s grounding in the conversation half.
- 03Introducing BDDSupportsThe article that named BDD; Given/When/Then proposed first as a documentation grammar, second as a testing grammar. The structural ancestor of Cucumber’s Gherkin.
- 04What is BDD?Supports“Using examples in conversation to illustrate behaviour.” The shortest accurate definition of the practice TS2 names.
- 05Conversational Patterns in BDDSupportsThe conversation-first idea. “BDD isn’t about the tools”. The spec is the discussion the team has, with the Gherkin file as its written record.
- 06Writing Better GherkinSupportsThe declarative-versus-imperative rule, in Cucumber’s own words. The shortest published source for the writing-style half of TS2.
- 07BDD OverviewSupportsCucumber’s three-practice idea: discovery, formulation, automation. SBE compressed into the loop a working team runs every sprint.
- 08The Cucumber Book, 2nd ed.SupportsThe reference text on putting the practice into a working test runner. Hellesøy created Cucumber; he and Wynne, with Steve Tooke, wrote the canonical book on operating it.
- 09The concept TS2 inherits from. The team’s ubiquitous language is the source of the “domain language” clause in the punch. The scenario is its written form.
- 10The post where Dinwiddie uses “three amigos”, a term he borrowed from friends at Nationwide: product owner, developer and tester agree how they will know a story is done, however and whenever they meet. The conversation half SBE depends on.
- 11SpecificationByExample (bliki)SupportsFowler says the phrase struck him at a workshop at XP/Agile Universe in 2002. Fowler’s idea: the example is what the spec turns into when it stops being abstract.
- 12The canonical glossary entry. Three perspectives in one room: business asks “what problem are we trying to solve?”, development asks “how might we build a solution to solve that problem?”, testing asks “what about this, what could possibly happen?”.
- 13Given, When, Then (bliki)SupportsThe clearest mainstream restatement of the Gherkin grammar as a documentation primitive. Fowler distinguishes the literary form from the test form. Both are SBE.
- 14What’s in a Story?SupportsThe original story template plus acceptance criteria as scenarios. The cross-pillar bridge between user-story practice and SBE’s declarative scenario.
- 15Anti-PatternsQualifiesNames two failure modes: feature-coupled step definitions and conjunction steps. The boundary between SBE and step-script imitation.
- 16Background for Wayne’s natural-language critique. Sets up the argument that example-based specs cannot encode invariants, only their instances.
- 17Integrated Tests Are A ScamOpposesTreats the integrated test SBE produces as a confidence-trick. Collaborators can drift while the scenario passes. The strongest published objection at the test-fidelity layer.
Seventeen sources, three stances. The supports above are the canon: Adzic, North, and Keogh on Specification by Example and BDD. The one qualifier, Cucumber's Anti-Patterns page, names two ways a step file goes wrong: step definitions that cannot be reused across features, and conjunction steps that combine several things into one. The opposers hold the steelman the reply has to address.
Examples
Before imperative step. Couples to the button copy "Submit".
When("I click the button with text {string}", async (label: string) => { await page.getByRole("button", { name: label }).click(); });
features/ rescue.featureScenario: A volunteer records a hedgehog rescue Given a hedgehog has arrived at the sanctuary When I click the button with text "Submit" Then the rescue is recorded
After declarative step. Describes the volunteer's intent.
When("I submit the hedgehog rescue form", async () => { await rescueForm.submit(page); });
features/ rescue.featureScenario: A volunteer records a hedgehog rescue Given a hedgehog has arrived at the sanctuary When I submit the hedgehog rescue form Then the rescue is recorded
Before imperative step. Couples to the button copy "Submit".
When("I click the button with text {string}", async (label: string) => { await page.getByRole("button", { name: label }).click(); });
features/ rescue.featureScenario: A volunteer records a hedgehog rescue Given a hedgehog has arrived at the sanctuary When I click the button with text "Submit" Then the rescue is recorded
After declarative step. Describes the volunteer's intent.
When("I submit the hedgehog rescue form", async () => { await rescueForm.submit(page); });
features/ rescue.featureScenario: A volunteer records a hedgehog rescue Given a hedgehog has arrived at the sanctuary When I submit the hedgehog rescue form Then the rescue is recorded
Before imperative step. Couples to the button copy "Submit".
When("I click the button with text {string}", async function (label) { await this.page.getByRole("button", { name: label }).click(); });
features/ rescue.featureScenario: A volunteer records a hedgehog rescue Given a hedgehog has arrived at the sanctuary When I click the button with text "Submit" Then the rescue is recorded
After declarative step. Describes the volunteer's intent.
When("I submit the hedgehog rescue form", async function () { await rescueForm.submit(this.page); });
features/ rescue.featureScenario: A volunteer records a hedgehog rescue Given a hedgehog has arrived at the sanctuary When I submit the hedgehog rescue form Then the rescue is recorded
Before imperative step. Couples to the button copy "Submit".
@when("I click the button with text {label}") def click_button_with_text(context, label: str) -> None: context.page.get_by_role("button", name=label).click()
features/ rescue.featureScenario: A volunteer records a hedgehog rescue Given a hedgehog has arrived at the sanctuary When I click the button with text "Submit" Then the rescue is recorded
After declarative step. Describes the volunteer's intent.
@when("I submit the hedgehog rescue form") def submit_rescue_form(context) -> None: rescue_form.submit(context.page)
features/ rescue.featureScenario: A volunteer records a hedgehog rescue Given a hedgehog has arrived at the sanctuary When I submit the hedgehog rescue form Then the rescue is recorded
Before imperative step. Couples to the button copy "Submit".
@When("I click the button with text {string}") public void clickButtonWithText(String label) { page.getByRole(AriaRole.BUTTON, new GetByRoleOptions().setName(label)).click(); }
src/ test/ resources/ features/ rescue.featureScenario: A volunteer records a hedgehog rescue Given a hedgehog has arrived at the sanctuary When I click the button with text "Submit" Then the rescue is recorded
After declarative step. Describes the volunteer's intent.
@When("I submit the hedgehog rescue form") public void submitRescueForm() { RescueForm.submit(page); }
src/ test/ resources/ features/ rescue.featureScenario: A volunteer records a hedgehog rescue Given a hedgehog has arrived at the sanctuary When I submit the hedgehog rescue form Then the rescue is recorded
Before imperative step. Couples to the button copy "Submit".
#[When("I click the button with text :label")] public function iClickTheButtonWithText(string $label): void { $this->getSession()->getPage()->pressButton($label); }
features/ rescue.featureScenario: A volunteer records a hedgehog rescue Given a hedgehog has arrived at the sanctuary When I click the button with text "Submit" Then the rescue is recorded
After declarative step. Describes the volunteer's intent.
#[When("I submit the hedgehog rescue form")] public function iSubmitTheHedgehogRescueForm(): void { RescueForm::submit($this->getSession()); }
features/ rescue.featureScenario: A volunteer records a hedgehog rescue Given a hedgehog has arrived at the sanctuary When I submit the hedgehog rescue form Then the rescue is recorded
Enforcement
Apply these rules in .gherkin-lintrc. The full enforcement across every tenet lives on the implementation page.
| Rule | Tool | Catches |
|---|---|---|
| no-unnamed-scenarios | gherkin-lint | scenarios without a name. The name is the headline; an unnamed scenario is documentation that doesn’t announce itself. |
| no-files-without-scenarios | gherkin-lint | feature files containing only background or rule blocks. A feature without a scenario doesn’t document a behaviour. |
| name-length | gherkin-lint | scenario names longer than 70 chars: the threshold beyond which the headline stops working as documentation. Adjust per house style. |
| use-and | gherkin-lint | consecutive Given/When/Then steps that should have used And for readability. The shape of the scenario is part of the spec. |
| playwright-bdd defineBddConfig | playwright-bdd | scenarios with no matching step definition. The build fails before the test runs; the spec stays honest about what is and isn’t automated. |
| @cucumber/cucumber --strict | @cucumber/cucumber | undefined or pending steps left in main. Strict mode fails the build until every step has an implementation. |
.gherkin-lintrcconfiguration snippet
{
"no-unnamed-features": "on",
"no-unnamed-scenarios": "on",
"no-empty-file": "on",
"no-files-without-scenarios": "on",
"indentation": ["on", { "Feature": 0, "Scenario": 2, "Step": 4, "Examples": 4 }],
"use-and": "on",
"name-length": ["on", { "Feature": 70, "Scenario": 70 }],
"no-multiple-empty-lines": "on",
"no-trailing-spaces": "on"
}Apply these rules in .gherkin-lintrc. The full enforcement across every tenet lives on the implementation page.
| Rule | Tool | Catches |
|---|---|---|
| no-unnamed-scenarios | gherkin-lint | scenarios without a name. The name is the headline; an unnamed scenario is documentation that doesn’t announce itself. |
| no-files-without-scenarios | gherkin-lint | feature files containing only background or rule blocks. A feature without a scenario doesn’t document a behaviour. |
| name-length | gherkin-lint | scenario names longer than 70 chars: the threshold beyond which the headline stops working as documentation. Adjust per house style. |
| use-and | gherkin-lint | consecutive Given/When/Then steps that should have used And for readability. The shape of the scenario is part of the spec. |
| playwright-bdd defineBddConfig | playwright-bdd | scenarios with no matching step definition. The build fails before the test runs; the spec stays honest about what is and isn’t automated. |
| @cucumber/cucumber --strict | @cucumber/cucumber | undefined or pending steps left in main. Strict mode fails the build until every step has an implementation. |
.gherkin-lintrcconfiguration snippet
{
"no-unnamed-features": "on",
"no-unnamed-scenarios": "on",
"no-empty-file": "on",
"no-files-without-scenarios": "on",
"indentation": ["on", { "Feature": 0, "Scenario": 2, "Step": 4, "Examples": 4 }],
"use-and": "on",
"name-length": ["on", { "Feature": 70, "Scenario": 70 }],
"no-multiple-empty-lines": "on",
"no-trailing-spaces": "on"
}Apply these rules in playwright.config.ts. The full enforcement across every tenet lives on the implementation page.
| Rule | Tool | Catches |
|---|---|---|
| no-unnamed-scenarios | gherkin-lint | scenarios without a name. |
| no-files-without-scenarios | gherkin-lint | feature files with no scenarios. |
| use-and | gherkin-lint | consecutive same-keyword steps that should use And. |
| playwright-bdd | playwright-bdd | scenarios without a matching step definition. |
| @cucumber/cucumber --strict | @cucumber/cucumber | undefined or pending steps in main. |
playwright.config.tsconfiguration snippet
import { defineConfig } from '@playwright/test';
import { defineBddConfig } from 'playwright-bdd';
const testDir = defineBddConfig({
features: 'features/*.feature',
steps: ['e2e/steps/**/*.ts'],
missingSteps: 'fail-on-gen'
});
export default defineConfig({
testDir,
reporter: [['list'], ['html', { outputFolder: 'reports/playwright' }]]
});Apply these rules in pyproject.toml. The full enforcement across every tenet lives on the implementation page.
| Rule | Tool | Catches |
|---|---|---|
| pytest-bdd --gherkin-terminal-reporter | pytest-bdd | low-fidelity scenario output. The Gherkin terminal reporter prints each scenario as a sentence: the artefact stakeholders can read directly. |
| pytest-bdd --generate-missing --feature features/ | pytest-bdd | undefined steps and unbound scenarios in committed feature files. The run prints the missing code and exits with status 100; the spec stays honest about what is automated. |
| behave --no-color --tags='not @wip' | Behave | WIP-tagged scenarios sneaking into production runs. Tag-based exclusions keep the regression suite distinct from the work-in-progress shelf. |
| behave --format=pretty | Behave | missing step-level visibility. The pretty formatter prints the Given/When/Then aloud; pair with the json formatter for a machine-readable export. |
| behave.runner / dry_run | Behave | step bindings that drift from the feature files. Dry-run loads steps without executing: useful pre-commit gate for mismatch detection. |
| gherkin-lint (npm) over Python features | gherkin-lint | feature-file shape regressions. The same Node-based linter runs against any .feature file regardless of the runner that consumes it. |
pyproject.tomlconfiguration snippet
[tool.pytest.ini_options]
bdd_features_base_dir = "features/"
[tool.behave]
default_format = "pretty"
default_tags = "not @wip"
verbose = trueApply these rules in junit-platform.properties. The full enforcement across every tenet lives on the implementation page.
| Rule | Tool | Catches |
|---|---|---|
| cucumber-junit-platform-engine | Cucumber-JVM | undefined or pending steps in committed feature files. Cucumber-JVM has run in strict mode by default since 6.0, and 7.0 removed the option to turn it off. The JUnit Platform engine reports an undefined step as a failed test; the spec stays honest. |
| html / json reporters | Cucumber-JVM | missing scenario-level visibility. The HTML reporter writes a static spec page; the JSON reporter feeds CI dashboards that double as the feature list. |
| PicoContainer / Spring step DI | Cucumber-JVM | step definitions sharing state via globals. DI containers force the boundary between scenario state and the application’s state. |
| Gauge --simple-console | Gauge | low-fidelity test output. The simple console formatter prints each spec name and step result as a line; the HTML report writes a sharable spec page. |
| Gauge concept files | Gauge | imperative step duplication. Concepts compose existing steps into named domain operations; the Gauge equivalent of a parameterised step. |
| gherkin-lint over JVM features | gherkin-lint | feature-file shape regressions for Cucumber-JVM projects. |
junit-platform.propertiesconfiguration snippet
cucumber.plugin=pretty, html:reports/cucumber.html, json:reports/cucumber.jsonApply these rules in behat.yml. The full enforcement across every tenet lives on the implementation page.
| Rule | Tool | Catches |
|---|---|---|
| behat --strict | Behat | undefined or pending steps left in main. Strict mode fails the build until every step has an implementation. |
| behat -f pretty | Behat | low-fidelity scenario output. The pretty formatter prints each scenario as a sentence: the artefact stakeholders can read directly. |
| behat -f junit | Behat | missing CI integration. JUnit XML output drives the same reports the unit-test suite produces; one feed for both halves of the pyramid. |
| behat --tags=~@wip | Behat | WIP-tagged scenarios sneaking into production runs. Tag-based exclusions separate the regression suite from the work-in-progress shelf. |
| gherkin-lint over Behat features | gherkin-lint | feature-file shape regressions for Behat projects. |
behat.ymlconfiguration snippet
default:
suites:
default:
paths:
- "%paths.base%/features"
contexts:
- FeatureContext
formatters:
pretty:
verbose: true
junit:
output_path: reports/behatAI rules
.cursor/rules/ts2-specification-by-example.mdc--- alwaysApply: true --- ## Prickles TS2: Specification by Example Write the scenario in the language a stakeholder uses. `When I submit the form` is the spec; `When I click the third button` is the implementation leaking through. Declarative steps describe behaviour. Imperative steps describe interaction. The first survives a redesign. The second breaks on every CSS edit. The Given/When/Then is the conversation, then the documentation, then the test. If it can't be read aloud at a Three Amigos meeting, rewrite it. One scenario, one behaviour. If the scenario uses `and` more than twice, split it.
Repo layout, CI, and ESLint wiring for these paths live on /implementation, not repeated on every tenet.
Counter-argument
The strongest pushback is Hillel Wayne's3: natural-language specs are ambiguous by design. A Gherkin scenario reads cleanly aloud and still means three things to three readers. The precision the test buys is precision against an example rather than against the behaviour. For systems whose correctness is genuinely bit-precise (cryptography, distributed consensus, billing) the scenario is a ladder rather than a roof. Michael Bolton sharpens the same point: he proposes calling an automated acceptance test a check. A passing check shows only that the program still works within the scope of the team's expectations.4 J. B. Rainsberger's broader objection5 is that the integrated test SBE produces gives false confidence. Collaborators can drift while the scenario passes.
Counter-argument retort
Hillel Wayne's objection3 is conceded inside its scope and refused outside it. For the systems whose correctness is genuinely bit-precise (cryptographic primitives, distributed consensus, financial settlement) the right artefact is a formal spec (TLA+, Alloy, a typed invariant) and the Gherkin sits below it as the customer-facing illustration. For the systems most working developers release most days (checkout flows, registration, scheduling, content management) the ambiguity Wayne names is real and small and the upside of a stakeholder-readable spec dominates the residual ambiguity. The scenario is not the roof but the ceiling tile that covers the room you live in.
Bolton's checking-versus-testing distinction4 is the strongest sharpening. The reply concedes and adds the practices that restore the questioning view. TS3 Parameterised Scenarios generalises the example into a table, and property-based testing generalises the table into a generator that produces examples the developer hadn't imagined.14 Exploratory testing kept on the team's books as a separate activity, not pretending to be regression, completes the picture. SBE is the regression-and-documentation half. The questioning half is its companion practice, not a substitute for it.
Rainsberger's “integrated tests are a scam”5 objection runs against treating SBE’s test as the only fidelity check. The reply concedes and adds contract testing at the boundaries. The scenario covers behaviour end-to-end, the contract test covers the interface. Both apply. Neither replaces the other.
The discipline reduces to four words on the conference room wall: write the example, agree the example, automate the example, run the example. The scenario is the spec. The spec is the contract. The contract is in CI. Same operating principle as TS1, focused on the form the artefact takes when stakeholders read it.
Notes
- [1]Gojko Adzic: Specification by Example: How Successful Teams Deliver the Right Software (Manning, 2011). Codifies the practice as seven process patterns and is the canonical reference for the term itself.
- [2]Cucumber documentation: “Writing Better Gherkin”. The declarative-versus-imperative rule in Cucumber’s own words: scenarios should describe the intended behaviour rather than the implementation.
- [3]Hillel Wayne: “Why Don’t People Use Formal Methods?” (hillelwayne.com, 2019). The natural-language-spec critique: examples cannot encode invariants, only their instances.
- [4]Michael Bolton: “Testing vs. Checking” (developsense.com, 2009), expanded from his Agile 2009 lightning talk. Checking confirms existing beliefs; testing looks for new information. He proposes that automated acceptance “tests” be called automated acceptance checks.
- [5]J. B. Rainsberger: “Integrated Tests Are A Scam” (thecodewhisperer.com, 2009). The integrated test SBE produces gives false confidence: a green suite exercises a vanishing fraction of the code paths.
- [6]Gojko Adzic: Bridging the Communication Gap: Specification by Example and Agile Acceptance Testing (Neuri, 2009). The earlier book that established the practice. Acceptance tests as agreed truth between business and developers.
- [7]Shane Hastie: “Interview and Book Review: Specification by Example” (InfoQ, 2011). Asked why he used the term rather than ATDD or BDD, Adzic answers: “It’s the one with the least amount of negative baggage.” He did not want ATDD, which he says focuses “too much on the testing aspect of the process”.
- [8]Dan North: “Introducing BDD” (dannorth.net, 2006). The article that named the practice. Given/When/Then proposed first as a documentation grammar, second as a testing grammar.
- [9]Eric Evans: Domain-Driven Design (Addison-Wesley, 2003). Ubiquitous Language is the source of the “domain language” clause in the TS2 punch. The scenario is the language’s written form.
- [10]Cucumber documentation: “History of BDD”. Traces the lineage from Evans’s ubiquitous language through North’s BDD into the modern Cucumber-era practice.
- [11]Liz Keogh: “What is BDD?” (lizkeogh.com, 2015). The shortest accurate definition: examples in conversation to illustrate behaviour.
- [12]George Dinwiddie: “If you don’t automate acceptance tests?” (blog.gdinwiddie.com, 2009). Dinwiddie uses “three amigos”, a term he says he borrowed from friends at Nationwide: product owner, developer and tester agree how they will know a story is done, however and whenever that conversation happens.
- [13]Matt Wynne & Aslak Hellesøy: The Cucumber Book, 2nd ed. (Pragmatic Bookshelf, 2017). The reference text on operating SBE with a working test runner.
- [14]David R. MacIver: “What is Property-Based Testing?” (hypothesis.works, 2016). Property-based testing as the next rung past parameterised scenarios: the table generalised into a generator that produces examples the developer hadn’t imagined.