Introduction
Modern QA teams need more than browser automation scripts. They also need tests that developers, testers, product owners, and business stakeholders can understand.
This is where Behavior Driven Development (BDD) and Cucumber become useful.
In this playwright bdd cucumber tutorial, you will learn how to combine Playwright browser automation with Cucumber and Gherkin to build a maintainable BDD automation framework using TypeScript.
A typical architecture looks like:
Gherkin Feature
↓
Step Definitions
↓
↓
Playwright
↓
Playwright provides browser automation, while Cucumber provides the BDD layer for writing executable specifications in Gherkin.
For teams that already use Playwright Automation Testing, Cucumber can add a business-readable layer on top of the automation framework.
This guide covers project setup, feature files, step definitions, hooks, Scenario Outlines, Page Object Model, test data, screenshots, traces, reporting, parallel execution, CI/CD, debugging, and interview preparation.
What Is Playwright BDD Cucumber?
Playwright BDD Cucumber is an automation approach that combines:
- Playwright for browser automation
- Cucumber for BDD execution
- Gherkin for readable scenarios
- TypeScript for implementation
- Page Object Model for maintainability
For example:
Feature: Login
Scenario: Successful login
Given I am on the login page
When I login with valid credentials
Then I should see the dashboard
The business-readable scenario is connected to TypeScript step definitions:
Given(‘I am on the login page’, async function () {
await this.page.goto(‘/login’);
});
This makes the test understandable without requiring every stakeholder to know TypeScript.
BDD, Cucumber, Gherkin, and Playwright Explained
These technologies have different responsibilities.
| Technology | Responsibility |
| BDD | Development/testing approach focused on behavior |
| Cucumber | Framework that executes Gherkin scenarios |
| Gherkin | Human-readable syntax |
| Playwright | Browser automation |
| TypeScript | Programming language |
| Page Object Model | Automation design pattern |
What is BDD?
BDD focuses on application behavior.
Instead of describing implementation details, you describe expected behavior.
For example:
Given a customer is logged in
When the customer adds a product
Then the product should appear in the cart
What is Cucumber?
Cucumber connects Gherkin scenarios with executable step definitions.
What is Gherkin?
Gherkin uses keywords such as:
Feature
Scenario
Given
When
Then
And
But
Background
Scenario Outline
Examples
What is Playwright?
Playwright is the browser automation layer.
It interacts with:
- Chromium
- Firefox
- WebKit
- Pages
- Locators
- Browser contexts
- APIs
The official Cucumber JavaScript documentation provides the Cucumber.js runtime and configuration concepts used for TypeScript-based Cucumber projects.
BDD vs TDD vs Traditional Automation
These approaches are related but different.
| Approach | Primary focus |
| TDD | Test-driven implementation |
| BDD | Application behavior |
| Traditional automation | Automated verification |
| Cucumber | Executable BDD specifications |
| Playwright | Browser/API automation |
BDD is particularly useful when QA, development, and business teams need a shared language.
However, not every Playwright project needs Cucumber.
For a small technical automation suite, native Playwright Test can be simpler.
Cucumber becomes more valuable when executable specifications and stakeholder-readable scenarios are important.
Why Use Cucumber with Playwright?
Combining Cucumber and Playwright provides several advantages.
1. Business-readable tests
When I search for “laptop”
Then I should see laptop products
is easier for non-developers to understand than raw automation code.
2. Reusable step definitions
A step can be reused across multiple feature files.
3. Scenario Outlines
The same scenario can run with different data.
4. Clear separation
Feature
↓
Steps
↓
Page Object
↓
Application
5. Useful reporting
Cucumber reports can show:
- Features
- Scenarios
- Steps
- Pass/fail status
- Execution duration
Playwright BDD Cucumber Project Setup
Create a project:
npm init -y
npm install -D @playwright/test
Install Cucumber:
npm install -D @cucumber/cucumber
Install TypeScript tooling:
npm install -D typescript ts-node
A practical structure is:
playwright-cucumber/
│
├── features/
│ ├── login.feature
│ └── ecommerce.feature
│
├── step-definitions/
│ ├── login.steps.ts
│ └── ecommerce.steps.ts
│
├── pages/
│ ├── LoginPage.ts
│ ├── ProductPage.ts
│ ├── CartPage.ts
│ └── CheckoutPage.ts
│
├── support/
│ ├── world.ts
│ └── hooks.ts
│
├── test-data/
│ └── users.json
│
├── cucumber.js
├── tsconfig.json
└── package.json
Installing Playwright, Cucumber, and TypeScript Dependencies
A useful package.json script is:
{
“scripts”: {
“bdd”: “cucumber-js –require-module ts-node/register –require \”step-definitions/**/*.ts\” –require \”support/**/*.ts\” \”features/**/*.feature\””
}
}
Then run:
npm run bdd
The exact Cucumber configuration can vary by project and Cucumber.js version, so keep your configuration aligned with the installed package version.
Creating the First Gherkin Feature File
Create:
features/login.feature
Add:
Feature: User Login
As a registered customer
I want to log in
So that I can access my account
Scenario: Successful login
Given I am on the login page
When I enter valid login credentials
And I click the login button
Then I should see the dashboard
This is a Playwright Gherkin example.
Notice that there is no TypeScript code in the feature file.
The feature describes behavior.
Writing Cucumber Step Definitions with Playwright
Create:
step-definitions/login.steps.ts
Example:
import { Given, When, Then } from ‘@cucumber/cucumber’;
import { expect } from ‘@playwright/test’;
Given(‘I am on the login page’, async function () {
await this.page.goto(‘/login’);
});
When(‘I enter valid login credentials’, async function () {
await this.page.getByLabel(‘Email’).fill(
process.env.TEST_USERNAME!
);
await this.page.getByLabel(‘Password’).fill(
process.env.TEST_PASSWORD!
);
});
When(‘I click the login button’, async function () {
await this.page.getByRole(‘button’, {
name: ‘Login’
}).click();
});
Then(‘I should see the dashboard’, async function () {
await expect(this.page).toHaveURL(/dashboard/);
});
The important relationship is:
Given → Given()
When → When()
Then → Then()
These are Playwright Cucumber step definitions.
Creating a Custom Cucumber World
Cucumber’s World object can hold scenario-specific state.
Create:
support/world.ts
import {
World,
IWorldOptions,
setWorldConstructor
} from ‘@cucumber/cucumber’;
import {
Browser,
BrowserContext,
Page
} from ‘@playwright/test’;
export class CustomWorld extends World {
browser!: Browser;
context!: BrowserContext;
page!: Page;
constructor(options: IWorldOptions) {
super(options);
}
}
setWorldConstructor(CustomWorld);
Now step definitions can access:
this.page
This avoids creating a global shared page that could cause test interference.
Playwright Cucumber Hooks and Test Lifecycle
Hooks are useful for browser setup and cleanup.
Create:
support/hooks.ts
import {
Before,
After
} from ‘@cucumber/cucumber’;
import { chromium } from ‘@playwright/test’;
Before(async function () {
this.browser = await chromium.launch({
headless: true
});
this.context = await this.browser.newContext();
this.page = await this.context.newPage();
});
After(async function () {
await this.page?.close();
await this.context?.close();
await this.browser?.close();
});
The lifecycle becomes:
Before
↓
Browser
↓
Context
↓
Page
↓
Scenario
↓
After
↓
Cleanup
This is one of the most important concepts when building a Playwright Cucumber Framework.
Page Object Model with Playwright BDD Cucumber
Avoid putting large amounts of browser logic directly inside step definitions.
Instead, create Page Objects.
LoginPage.ts
import { Page } from ‘@playwright/test’;
export class LoginPage {
constructor(private page: Page) {}
async open() {
await this.page.goto(‘/login’);
}
async login(
username: string,
password: string
) {
await this.page.getByLabel(‘Email’)
.fill(username);
await this.page.getByLabel(‘Password’)
.fill(password);
await this.page.getByRole(‘button’, {
name: ‘Login’
}).click();
}
}
Step definition:
import { Given, When } from ‘@cucumber/cucumber’;
import { LoginPage } from ‘../pages/LoginPage’;
Given(‘I am on the login page’, async function () {
this.loginPage = new LoginPage(this.page);
await this.loginPage.open();
});
When(‘I login with valid credentials’, async function () {
await this.loginPage.login(
process.env.TEST_USERNAME!,
process.env.TEST_PASSWORD!
);
});
Now your architecture is cleaner:
Feature
↓
Step Definition
↓
Page Object
↓
Playwright
Managing Test Data and Scenario Outlines
Scenario Outlines are one of the most useful Cucumber features for data-driven BDD.
Example:
Scenario Outline: Login with different credentials
Given I am on the login page
When I login with “<username>” and “<password>”
Then I should see “<result>”
Examples:
| username | password | result |
| user@example.com | Valid123 | Dashboard |
| user@example.com | Wrong123 | Invalid Login |
| unknown@example.com | Valid123 | Invalid Login |
This creates three executions from one scenario.
The corresponding step definition:
When(
‘I login with {string} and {string}’,
async function (
username: string,
password: string
) {
await this.loginPage.login(
username,
password
);
}
);
This is an excellent Playwright BDD example for parameterized testing.
Background, Tags, Given, When, Then, and Examples
Background
Use Background when every scenario needs the same setup.
Background:
Given I am on the login page
Tags
Tags allow you to categorize scenarios:
@smoke
Scenario: Successful login
npx cucumber-js –tags “@smoke”
You can also use:
@regression
and:
@ecommerce
Given
Represents the initial state.
Given I am logged in
When
Represents an action.
When I add the laptop to the cart
Then
Represents expected behavior.
Then I should see the laptop in the cart
Examples
Provides data for Scenario Outlines.
Real-World Login and E-Commerce BDD Example
Create:
features/ecommerce.feature
Feature: E-Commerce Shopping
Background:
Given I am logged in as a customer
@smoke
Scenario: Search for a product
When I search for “laptop”
Then I should see laptop products
@regression
Scenario: Add product to cart
When I search for “laptop”
And I select the first product
And I add the product to the cart
Then the product should appear in the cart
Scenario Outline: Invalid login
Given I am on the login page
When I login with “<username>” and “<password>”
Then I should see the “<message>” message
Examples:
| username | password | message |
| wrong@example.com | Wrong123 | Invalid credentials |
| unknown@example.com | Test123 | Invalid credentials |
This demonstrates:
- Background
- Tags
- Scenario
- Scenario Outline
- Examples
- Given
- When
- Then
- Reusable business scenarios
Playwright Cucumber Fixtures and Reusable Components
Cucumber uses the World object and hooks for scenario state.
You can centralize:
- Browser
- Context
- Page
- API clients
- Page Objects
- Test data
For example:
Before(async function () {
this.browser = await chromium.launch();
this.context = await this.browser.newContext();
this.page = await this.context.newPage();
this.loginPage = new LoginPage(this.page);
this.productPage = new ProductPage(this.page);
this.cartPage = new CartPage(this.page);
});
This gives every scenario reusable components.
Avoid putting application logic directly into hooks.
Hooks should handle setup and cleanup, while Page Objects should handle application behavior.
Screenshots, Traces, Videos, and Reporting
When a BDD scenario fails, debugging should be easy.
Screenshot on failure
In an After hook:
After(async function (scenario) {
if (scenario.result?.status === ‘FAILED’) {
const screenshot = await this.page.screenshot();
await this.attach(
screenshot,
‘image/png’
);
}
await this.page?.close();
await this.context?.close();
await this.browser?.close();
});
Now the Cucumber report can contain the failure screenshot.
Tracing
You can start tracing:
Before(async function () {
this.browser = await chromium.launch();
this.context = await this.browser.newContext();
await this.context.tracing.start({
screenshots: true,
snapshots: true
});
this.page = await this.context.newPage();
});
Stop it after execution:
After(async function () {
await this.context.tracing.stop({
path: `test-results/trace-${Date.now()}.zip`
});
await this.browser.close();
});
Playwright Trace Viewer is useful for investigating:
- Failed locators
- Navigation
- Screenshots
- Timing
- DOM state
- Actions
For Playwright-specific debugging, traces can provide much more information than a simple error message.
HTML Reporting
Cucumber can produce JSON output:
npx cucumber-js \
–format json:test-results/cucumber-report.json
That JSON can then be converted into an HTML report using a Cucumber-compatible reporting tool.
A typical CI artifact structure is:
test-results/
├── cucumber-report.json
├── screenshots/
└── traces/
Your report should make it easy to answer:
- Which feature failed?
- Which scenario failed?
- Which step failed?
- Which test data was used?
- What screenshot was captured?
- Is a trace available?
Parallel Execution and Cross-Browser Testing
Playwright supports Chromium, Firefox, and WebKit.
Your Cucumber framework can create a browser based on configuration:
const browser = await chromium.launch();
For Firefox:
import { firefox } from ‘@playwright/test’;
const browser = await firefox.launch();
For WebKit:
import { webkit } from ‘@playwright/test’;
const browser = await webkit.launch();
For parallel Cucumber execution, make sure each scenario has isolated:
- Browser context
- Page
- Test data
- Accounts
- Orders
- Files
Avoid global shared state.
For example, this is dangerous:
Global Page
Global Browser Context
Shared Shopping Cart
Instead:
Worker/Scenario
↓
Own Browser Context
↓
Own Page
↓
Own Test Data
This is particularly important for e-commerce workflows.
Playwright BDD Cucumber CI/CD Integration
A basic GitHub Actions workflow can look like:
name: Playwright BDD Tests
on:
push:
branches: [main]
pull_request:
jobs:
bdd-tests:
runs-on: ubuntu-latest
steps:
– name: Checkout
uses: actions/checkout@v6
– name: Setup Node
uses: actions/setup-node@v6
with:
node-version: lts/*
– name: Install dependencies
run: npm ci
– name: Install Playwright browsers
run: npx playwright install –with-deps
– name: Run Cucumber tests
run: npm run bdd
env:
BASE_URL: ${{ secrets.BASE_URL }}
TEST_USERNAME: ${{ secrets.TEST_USERNAME }}
TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
– name: Upload test results
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v5
with:
name: cucumber-results
path: test-results/
Playwright’s CI documentation covers browser installation and CI execution patterns, while Cucumber.js handles execution of the Gherkin scenarios.
CI/CD best practices
Use:
- Environment variables
- CI secrets
- Dedicated test accounts
- Stable test data
- Headless browsers
- Screenshots on failure
- Traces on failure
- Test artifacts
- Smoke and regression tags
Real-World Playwright Cucumber Automation Framework Project
For a portfolio project, create:
ecommerce-playwright-bdd/
│
├── features/
│ ├── login.feature
│ ├── products.feature
│ ├── cart.feature
│ └── checkout.feature
│
├── step-definitions/
│ ├── login.steps.ts
│ ├── product.steps.ts
│ ├── cart.steps.ts
│ └── checkout.steps.ts
│
├── pages/
│ ├── LoginPage.ts
│ ├── ProductPage.ts
│ ├── CartPage.ts
│ └── CheckoutPage.ts
│
├── support/
│ ├── world.ts
│ └── hooks.ts
│
├── test-data/
│ ├── users.json
│ └── products.json
│
├── test-results/
├── cucumber.js
└── package.json
Login feature
Test:
- Valid login
- Invalid login
- Locked account
- Empty credentials
Product feature
Test:
- Product search
- Category selection
- Product details
- Product sorting
Cart feature
Test:
- Add product
- Remove product
- Update quantity
- Verify total
Checkout feature
Test:
- Shipping information
- Order summary
- Payment workflow
- Order confirmation
Scenario Outline
Use multiple products:
Scenario Outline: Add different products
Given I am logged in
When I search for “<product>”
And I add the product to the cart
Then the cart should contain “<product>”
Examples:
| product |
| Laptop |
| Headphones |
| Smartphone |
This project demonstrates genuine Playwright BDD Testing rather than a simple login script.
Common Playwright BDD Cucumber Errors and Solutions
| Problem | Likely cause | Solution |
| Undefined step | Step text doesn’t match definition | Check wording |
| Duplicate step | Multiple matching definitions | Remove or rename one |
| this.page undefined | World not configured | Check custom World |
| Browser not closed | Missing After hook | Add cleanup |
| Login state leaks | Shared context | Create context per scenario |
| Screenshot missing | Failure hook issue | Check attach() |
| Feature not found | Incorrect path | Check Cucumber config |
| TypeScript not loaded | Missing ts-node/config | Verify require setup |
| CI browser failure | Browser dependencies missing | Install with Playwright |
| Tests fail in parallel | Shared state | Isolate data and contexts |
Playwright BDD Cucumber Best Practices
Use Gherkin for behavior
Don’t write implementation details:
When I click #submit
Prefer:
When I submit the login form
Keep step definitions thin
Avoid putting hundreds of lines into a step file.
Use:
Step
↓
Page Object
↓
Action
Avoid duplicate steps
Create reusable business-level steps.
Use Scenario Outlines for similar scenarios
Don’t create ten almost-identical feature scenarios when Examples can express the variation.
Use tags
For example:
@smoke
@regression
@checkout
@authentication
Keep hooks focused
Hooks should manage lifecycle, not business behavior.
Use Page Object Model
Centralize locators and application actions.
Isolate test data
Parallel scenarios should not depend on one shared account or shopping cart.
Capture failure artifacts
Use:
- Screenshots
- Traces
- Videos when required
- Cucumber reports
Protect credentials
Use environment variables and CI/CD secrets.
Don’t overuse Cucumber
If your team doesn’t need business-readable Gherkin, native Playwright Test may provide a simpler architecture.
Playwright BDD Cucumber Interview Questions with Answers
1. What is Playwright BDD Cucumber?
It combines Playwright browser automation with Cucumber’s BDD execution model and Gherkin feature files.
2. What is Gherkin?
Gherkin is a structured language used to describe application behavior with keywords such as Given, When, Then, Scenario, and Feature.
3. What is a step definition?
A step definition connects a Gherkin statement to executable TypeScript code.
4. What is the difference between Cucumber and Playwright?
Cucumber provides the BDD/Gherkin layer.
Playwright performs browser automation.
5. How do you use Page Object Model with Cucumber?
Step definitions call Page Object methods instead of directly containing all locator and browser logic.
6. What is Scenario Outline?
A Scenario Outline allows the same Gherkin scenario to execute with multiple datasets provided through an Examples table.
7. What are Cucumber hooks?
Hooks such as Before and After execute setup and cleanup around scenarios.
8. How do you handle screenshots?
Capture a screenshot in an After hook when the scenario fails and attach it to the Cucumber report.
9. How do you execute only smoke tests?
Use tags:
npx cucumber-js –tags “@smoke”
10. How do you make Cucumber Playwright tests parallel-safe?
Create isolated browser contexts, pages, accounts, and test data for independent scenarios.
Learning Roadmap for Beginners
If you are new to Playwright BDD Cucumber, follow this order.
Step 1: Learn Playwright
Start with:
- Locators
- Assertions
- Browser
- Context
- Page
- Auto-waiting
Step 2: Learn TypeScript
Focus on:
- Classes
- Interfaces
- Async/await
- Modules
Step 3: Learn Gherkin
Practice:
Feature
Scenario
Given
When
Then
And
Background
Examples
Step 4: Learn Cucumber
Understand:
- Step definitions
- World
- Hooks
- Tags
- Scenario Outlines
Step 5: Combine Cucumber and Playwright
Build:
Feature
↓
Step
↓
POM
↓
Playwright
Step 6: Add framework features
Learn:
- Test data
- Authentication
- Fixtures
- API testing
- Reporting
- Screenshots
- Tracing
Step 7: Learn CI/CD
Practice:
- GitHub Actions
- Docker
- Secrets
- Artifacts
- Parallel execution
Step 8: Build the e-commerce project
Publish the sanitized project on GitHub.
This demonstrates practical Playwright Cucumber Framework with TypeScript skills.
FAQs: Playwright BDD Cucumber Tutorial
What is Playwright BDD Cucumber?
Playwright BDD Cucumber combines Playwright browser automation with Cucumber’s BDD framework and Gherkin syntax.
How do I get started with Playwright Cucumber?
Install Playwright, Cucumber.js, and TypeScript, create a .feature file, implement its step definitions, and connect the steps to Playwright Page Objects.
Can Playwright work with Cucumber?
Yes. Playwright can be used as the browser automation engine while Cucumber.js manages Gherkin scenarios and step definitions.
What is the difference between Playwright and Cucumber?
Playwright automates browsers. Cucumber executes behavior specifications written in Gherkin.
Can I use TypeScript with Playwright Cucumber?
Yes. TypeScript can be used for step definitions, hooks, Page Objects, custom World implementations, and utilities.
Can Playwright Cucumber run in CI/CD?
Yes. You can execute Cucumber scenarios in GitHub Actions, Jenkins, Azure DevOps, or other CI systems.
Can I use Page Object Model with Cucumber?
Yes. POM is a common way to keep browser interaction logic out of Gherkin step definitions.
How do I run a specific Cucumber tag?
For example:
npx cucumber-js –tags “@smoke”
Is Cucumber required for Playwright?
No.
Playwright can be used directly with Playwright Test. Cucumber is useful when the project benefits from BDD and Gherkin-based specifications.
