UI はローカライズ済みです。スキルの紹介と説明文書は選択した言語へ AI が自動翻訳し、コード・コマンド・パス・専門用語は原文のままです。原作者の文書が優先されます。
スキルを探す
開発・技術 · SKILL.md

cucumber

cucumber

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 …

Independent GitHub snapshot · Automated classification · Original documentation governs

Tterminalskills / skillsApache-2.0

スキルの説明

GitHub

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)

# 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

# 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

// 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

// 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);
// 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

// 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

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

# .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:

npx cucumber-js
.......................

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?"

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
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):

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.
保存したスキル