Skip to content
Tugkan Boz edited this page Jun 4, 2026 · 1 revision

BDD

Two ways to write given / when / then with two-go.

two-go/bdd (no extra dependency)

A small, runner-agnostic layer. scenario(steps) returns an async function you hand to your runner's test(). Steps share a world object.

import { test } from "node:test";
import { go } from "two-go";
import { scenario, given, when, then, and } from "two-go/bdd";

const api = go("https://api.example.com");

test("creating a user", scenario([
  given("a valid payload", (w) => { w.payload = { name: "Ada" }; }),
  when("the user is created", async (w) => { w.res = await api.post("/users").json(w.payload); }),
  then("the response is 201", (w) => w.res.expectStatus(201)),
  and("the body echoes the name", (w) => w.res.expectJson("name", "Ada")),
]));

feature(name, scenarios) builds a labelled list you can loop and register:

import { feature } from "two-go/bdd";

for (const s of feature("Login", {
  "valid credentials": [ given(...), when(...), then(...) ],
  "wrong password": [ when(...), then(...) ],
})) {
  test(s.name, s.run);
}

A when stashes the response on the world, a then asserts on it. Works in node:test, Jest, Vitest, and Mocha.

Real Gherkin with cucumber-js

If you want .feature files, use cucumber-js and call two-go inside the step definitions:

Scenario: Get an existing user
  Given a user named "Grace" exists
  When I get that user by id
  Then the response status should be 200
When("I get that user by id", async function () {
  this.res = await this.api.get(`/users/${this.userId}`);
});
Then("the response status should be {int}", function (status) {
  this.res.expectStatus(status);
});

A full runnable cucumber example (with Scenario Outline, tags, and an HTML report) lives in two-go-examples/cucumber. There is also an e-commerce BDD suite with 20 scenarios in two-go-examples/ecommerce-bdd.

Clone this wiki locally