Skip to content
Back to skills

Cucumber

ASecurity

Cucumber runs behavior-driven (BDD) tests written as plain-language Gherkin feature files and matches each Given/When/Then step to code. Use when the user wants to write feature files, implement step definitions in JavaScript or TypeScript with cucumber-js, share state through a World, add hooks, tags and profiles, or run BDD scenarios in CI. Also use when the user mentions "cucumber," "BDD," "Gherkin," "feature files," "given-when-then," "step definitions," or "behavior-driven." For contract...

  • 142 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added May 27, 2026
developmentjavascripttypescriptgojavabashnodetestinggitapidocumentation

Works with

  • terminal
  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 2 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add TerminalSkills/skills --skill cucumber --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cucumber?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Cucumber
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/terminalskills-cucumber/badge)](https://www.skillsdirectory.com/skills/terminalskills-cucumber)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: cucumber
description: >-
  Cucumber runs behavior-driven (BDD) tests written as plain-language Gherkin
  feature files and matches each Given/When/Then step to code. Use when the
  user wants to write feature files, implement step definitions in JavaScript
  or TypeScript with cucumber-js, share state through a World, add hooks, tags
  and profiles, or run BDD scenarios in CI. Also use when the user mentions
  "cucumber," "BDD," "Gherkin," "feature files," "given-when-then," "step
  definitions," or "behavior-driven." For contract testing, see pact.
license: Apache-2.0
compatibility: "Node.js 22, 24 or 26+ (cucumber-js 13)"
metadata:
  author: terminal-skills
  version: "1.1.0"
  category: development
  repository: https://github.com/cucumber/cucumber-js
  tags:
    - bdd
    - gherkin
    - acceptance-testing
    - step-definitions
---

# Cucumber

## Overview

Cucumber executes specifications written in Gherkin: each `Given`/`When`/`Then` line in a `.feature` file is matched to a step definition function, so scenarios double as living documentation that developers, testers and stakeholders can all read. This skill covers cucumber-js (`@cucumber/cucumber`, version 13): feature files, step definitions in TypeScript, the World and hooks, profiles, filtering by tag, and CI.

## Instructions

### Initial Assessment

1. **Language** — JavaScript or TypeScript (this skill covers cucumber-js; other languages have their own Cucumber implementations)
2. **Scope** — API testing, UI testing, or both?
3. **Team** — Who writes feature files? (developers, QA, product)
4. **Existing tests** — Adding BDD to an existing suite or starting fresh?

### Setup (JavaScript)

```bash
# Install Cucumber.js with TypeScript support. Install it per project, never globally.
npm install --save-dev @cucumber/cucumber tsx typescript @types/node
mkdir -p features/step_definitions features/support
```

### Feature File

```gherkin
# features/login.feature
@login
Feature: User login
  As a registered user
  I want to log in to my account
  So that I can reach my dashboard

  Background:
    Given a registered user "dana.reyes@fernhill.dev" with password "Tr4il-Mix-88"

  @smoke
  Scenario: Successful login with valid credentials
    When I log in as "dana.reyes@fernhill.dev" with password "Tr4il-Mix-88"
    Then I should be redirected to "/dashboard"
    And I should see the message "Welcome back"

  Scenario: Failed login with a wrong password
    When I log in as "dana.reyes@fernhill.dev" with password "tr4il-mix"
    Then I should see the error "Invalid credentials"

  Scenario Outline: Login validation
    When I log in as "<email>" with password "<password>"
    Then I should see the error "<error>"

    Examples:
      | email                   | password     | error                |
      |                         | Tr4il-Mix-88 | Email is required    |
      | dana.reyes@fernhill.dev |              | Password is required |
      | not-an-email            | Tr4il-Mix-88 | Invalid email format |
```

### Step Definitions

```typescript
// features/step_definitions/login.steps.ts — maps Gherkin steps to code.
// Use `function`, not arrow functions: Cucumber binds the World to `this`.
import assert from 'node:assert/strict';
import { Given, When, Then } from '@cucumber/cucumber';
import type { AppWorld } from '../support/world';

Given('a registered user {string} with password {string}',
  function (this: AppWorld, email: string, password: string) {
    this.auth.register(email, password);
  });

When('I log in as {string} with password {string}',
  function (this: AppWorld, email: string, password: string) {
    this.result = this.auth.login(email, password);
  });

Then('I should be redirected to {string}', function (this: AppWorld, path: string) {
  assert.equal(this.result?.redirect, path);
});

Then('I should see the message {string}', function (this: AppWorld, message: string) {
  assert.equal(this.result?.message, message);
});

Then('I should see the error {string}', function (this: AppWorld, error: string) {
  assert.equal(this.result?.ok, false);
  assert.equal(this.result?.error, error);
});
```

### Hooks and World

```typescript
// features/support/world.ts — a fresh World instance is created for every scenario.
import { setWorldConstructor, World } from '@cucumber/cucumber';
import { AuthService, type LoginResult } from '../../src/auth';

export interface Params { appUrl: string }

export class AppWorld extends World<Params> {
  auth = new AuthService();
  result?: LoginResult;
}

setWorldConstructor(AppWorld);
```

```typescript
// features/support/hooks.ts — setup and teardown around scenarios.
import { After, Before, BeforeAll, Status, setDefaultTimeout } from '@cucumber/cucumber';
import type { AppWorld } from './world';

setDefaultTimeout(10_000); // steps and hooks time out after 5000 ms by default

BeforeAll(async function () {
  // one-time setup; no World here, but this.parameters is available
});

Before({ tags: '@smoke' }, async function (this: AppWorld) {
  this.log(`running against ${this.parameters.appUrl}`);
});

After(async function (this: AppWorld, { result, pickle }) {
  if (result?.status === Status.FAILED) {
    this.attach(JSON.stringify(this.result, null, 2), 'application/json');
    this.log(`failed: ${pickle.name}`);
  }
});
```

### Cucumber Configuration

```javascript
// cucumber.js — profiles for a CommonJS project (no "type": "module" in package.json).
const common = {
  requireModule: ['tsx/cjs'],
  require: ['features/support/**/*.ts', 'features/step_definitions/**/*.ts'],
  worldParameters: { appUrl: process.env.APP_URL || 'http://localhost:3000' },
};

module.exports = {
  default: {
    ...common,
    format: ['progress', ['html', 'reports/cucumber.html']],
  },
  ci: {
    ...common,
    format: ['summary', ['junit', 'reports/cucumber.xml'], ['message', 'reports/cucumber.ndjson']],
    tags: 'not @wip',
    retry: 1,
  },
};
```

In an ESM project (`"type": "module"`), write the profiles as `export default { … }` and `export const ci = { … }`, and load TypeScript with `import: ['./tsx-register.js', 'features/**/*.ts']` instead of `requireModule`/`require`, where `tsx-register.js` contains `import { register } from 'tsx/esm/api'; register()`. Configuration can also live in `cucumber.json`, `cucumber.yaml` or `cucumber.mjs`.

### Running Cucumber

```bash
npx cucumber-js                       # all features (features/**/*.feature), default profile
npx cucumber-js --profile ci          # a named profile
npx cucumber-js --tags "@smoke"       # by tag
npx cucumber-js --tags "@login and not @wip"
npx cucumber-js features/login.feature
npx cucumber-js features/login.feature:12   # the scenario at line 12
npx cucumber-js --name "validation"   # scenarios whose name matches the regex
npx cucumber-js --dry-run             # check that every step has a definition
npx cucumber-js --parallel 4          # worker threads
npx cucumber-js --shard 1/3           # split a run across CI jobs
npx cucumber-js --fail-fast
```

### CI Integration

```yaml
# .github/workflows/cucumber.yml — run BDD tests and keep the reports.
name: BDD Tests
on: [push]
jobs:
  cucumber:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
      - run: npm ci
      - run: npx cucumber-js --profile ci
      - uses: actions/upload-artifact@v7
        if: always()
        with:
          name: cucumber-report
          path: reports/
```

## Examples

### Example 1: Add a BDD suite for login to a TypeScript project

User: "Write Cucumber tests for our login service and run them."

Create the feature file, step definitions, World, hooks and `cucumber.js` shown above (the steps call an `AuthService` class in `src/auth.ts` with `register` and `login` methods), then:

```bash
npx cucumber-js
```

```text
.......................

1 hook (1 passed)
5 scenarios (5 passed)
22 steps (22 passed)
0m 0.8s (0m 0.0s executing your code)
```

The outline expands to three scenarios, and `reports/cucumber.html` holds the HTML report. `npx cucumber-js --tags "@smoke" --format pretty` prints the single smoke scenario step by step with a ✔ per step.

### Example 2: Turn a new scenario from the product owner into step stubs

User: "Product added features/cart.feature. What code do I need to write?"

```gherkin
Feature: Cart
  Scenario: Add an item
    Given an empty cart
    When I add 2 units of "SKU-4417"
    Then the cart total should be 59.98
```

```bash
npx cucumber-js features/cart.feature
```

Steps without a definition are reported as undefined, the run exits with code 1, and Cucumber prints ready-to-paste snippets (shortened here: for the number `2` it also prints a second `When` snippet with `{float}` — keep only one of the two, or the step becomes ambiguous):

```text
1 scenario (1 undefined)
4 steps (1 passed, 3 undefined)

You can implement missing steps with the snippets below:

Given('an empty cart', function () {
  // Write code here that turns the phrase above into concrete actions
  return 'pending';
});

When('I add {int} units of {string}', function (int, string) {
  // Write code here that turns the phrase above into concrete actions
  return 'pending';
});

Then('the cart total should be {float}', function (float) {
  // Write code here that turns the phrase above into concrete actions
  return 'pending';
});
```

Paste them into `features/step_definitions/cart.steps.ts`, add types, and replace `return 'pending'` with real code.

## Guidelines

- **Describe behavior, not clicks.** "When I log in as …" survives a redesign; "When I click the blue button" does not. Aim for 3–5 steps per scenario and keep the mechanics inside step definitions.
- **Keep state in the World**, not in module-level variables: every scenario (and every retry) gets a fresh instance, which also makes `--parallel` safe.
- **No arrow functions** for steps and hooks that use `this`; with arrows, import `world` from `@cucumber/cucumber` instead.
- **Use `tsx`, not `ts-node`.** The cucumber-js docs recommend it, and `ts-node/register` crashes with TypeScript 7.
- **Removed options fail the run.** `publishQuiet` / `--publish-quiet` was removed in v12 and the CLI rejects unknown options; delete it from old configs. v13 dropped Node.js 20 and the `Cli` export (use `runCucumber` from `@cucumber/cucumber/api`).
- **Parallel mode** runs `BeforeAll`/`AfterAll` once per worker. For one-time setup such as starting a server, use `BeforeAll({ on: HookTarget.COORDINATOR }, …)` (v13.2+).
- **Reports:** the built-in `json` formatter is in maintenance mode; prefer `message` (NDJSON), `junit` or `html`. On the command line write a formatter with a path as `--format "html":"reports/cucumber.html"`.
- **Retries hide flakiness.** Limit them with `retryTagFilter: '@flaky'` rather than retrying everything.
- **Pending and undefined steps fail the run** (`strict` is on by default); tag unfinished work `@wip` and exclude it with `--tags "not @wip"`.
- **Real credentials** (staging accounts, API tokens) belong in environment variables read in `cucumber.js` or hooks; feature files are shared widely and should only contain seeded test data.
- **When not to use it:** if nobody outside engineering reads the scenarios, plain unit or end-to-end tests are cheaper to maintain than a Gherkin layer.

Files in this skill

  • SKILL.md7 KB
  • _scores.json1.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…