Chapter I - Introduction to BDD and Cucumber
(avr. time for this chapter: 1 day)
Cucumber is a tool that supports Behavior-Driven Development (BDD). It allows you to write tests in plain language that non-technical stakeholders can understand, bridging the gap between business requirements and technical implementation.
What is BDD?
Behavior-Driven Development is a software development approach that:
- Focuses on the behavior of the system from the user's perspective
- Uses a shared language between developers, testers, and business stakeholders
- Creates living documentation that stays in sync with the code
- Encourages collaboration before implementation
BDD vs TDD
| Aspect | TDD | BDD |
|---|---|---|
| Focus | Code correctness | System behavior |
| Language | Technical (code) | Natural language |
| Audience | Developers | Everyone |
| Scope | Unit level | Feature level |
| Documentation | Code comments | Feature files |
Gherkin Syntax
Gherkin is the language used to write Cucumber tests. It uses a specific set of keywords.
Keywords
Feature: A high-level description of a software feature
As a [role]
I want [feature]
So that [benefit]
Background: Steps that run before each scenario
Given some precondition
Scenario: A specific example of the feature
Given some context
When some action is performed
Then some outcome should occur
Scenario Outline: A template for multiple examples
Given I have <count> items
When I add <more> items
Then I should have <total> items
Examples:
| count | more | total |
| 1 | 2 | 3 |
| 5 | 3 | 8 |
Step Keywords
- Given: Describes the initial context (preconditions)
- When: Describes the action being performed
- Then: Describes the expected outcome
- And/But: Continues the previous step type
Setting Up Cucumber
JavaScript/TypeScript Setup
# Create project
mkdir cucumber-demo
cd cucumber-demo
npm init -y
# Install Cucumber
npm install --save-dev @cucumber/cucumber
# Install TypeScript support (optional)
npm install --save-dev typescript ts-node @types/node
Project Structure
cucumber-demo/
├── features/
│ ├── login.feature
│ ├── cart.feature
│ └── step_definitions/
│ ├── login.steps.ts
│ └── cart.steps.ts
├── support/
│ └── world.ts
├── cucumber.js
├── tsconfig.json
└── package.json
Configuration
Create cucumber.js:
module.exports = {
default: {
requireModule: ['ts-node/register'],
require: ['features/step_definitions/**/*.ts', 'support/**/*.ts'],
format: [
'progress-bar',
'html:reports/cucumber-report.html',
'json:reports/cucumber-report.json'
],
formatOptions: { snippetInterface: 'async-await' },
publishQuiet: true
}
};
Create tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"outDir": "./dist",
"rootDir": "."
},
"include": ["features/**/*", "support/**/*"]
}
Your First Feature
Writing a Feature File
Create features/login.feature:
Feature: User Login
As a registered user
I want to log in to my account
So that I can access my personal dashboard
Background:
Given I am on the login page
Scenario: Successful login with valid credentials
When I enter "test@example.com" as email
And I enter "password123" as password
And I click the login button
Then I should be redirected to the dashboard
And I should see a welcome message
Scenario: Failed login with invalid password
When I enter "test@example.com" as email
And I enter "wrongpassword" as password
And I click the login button
Then I should see an error message "Invalid credentials"
And I should remain on the login page
Scenario: Failed login with unregistered email
When I enter "unknown@example.com" as email
And I enter "password123" as password
And I click the login button
Then I should see an error message "User not found"
Running to Generate Step Definitions
npx cucumber-js
Cucumber will output undefined step snippets that you can copy.
Implementing Step Definitions
Create features/step_definitions/login.steps.ts:
import { Given, When, Then } from '@cucumber/cucumber';
import { expect } from 'chai';
// For now, we'll use simple state management
// Later chapters will integrate with Playwright/Cypress
let currentPage = '';
let currentEmail = '';
let currentPassword = '';
let errorMessage = '';
let isLoggedIn = false;
Given('I am on the login page', function () {
currentPage = 'login';
});
When('I enter {string} as email', function (email: string) {
currentEmail = email;
});
When('I enter {string} as password', function (password: string) {
currentPassword = password;
});
When('I click the login button', function () {
// Simulate login logic
if (currentEmail === 'test@example.com' && currentPassword === 'password123') {
isLoggedIn = true;
currentPage = 'dashboard';
errorMessage = '';
} else if (currentEmail === 'unknown@example.com') {
errorMessage = 'User not found';
} else {
errorMessage = 'Invalid credentials';
}
});
Then('I should be redirected to the dashboard', function () {
expect(currentPage).to.equal('dashboard');
});
Then('I should see a welcome message', function () {
expect(isLoggedIn).to.be.true;
});
Then('I should see an error message {string}', function (message: string) {
expect(errorMessage).to.equal(message);
});
Then('I should remain on the login page', function () {
expect(currentPage).to.equal('login');
});
Running Tests
# Run all features
npx cucumber-js
# Run specific feature
npx cucumber-js features/login.feature
# Run specific scenario by name
npx cucumber-js --name "Successful login"
# Run with tags
npx cucumber-js --tags "@smoke"
Tags
Tags help organize and filter scenarios.
Adding Tags
@login @authentication
Feature: User Login
@smoke @critical
Scenario: Successful login with valid credentials
Given I am on the login page
...
@negative
Scenario: Failed login with invalid password
Given I am on the login page
...
@wip
Scenario: Login with two-factor authentication
Given I am on the login page
...
Running with Tags
# Run scenarios with specific tag
npx cucumber-js --tags "@smoke"
# Run scenarios with multiple tags (AND)
npx cucumber-js --tags "@smoke and @critical"
# Run scenarios with either tag (OR)
npx cucumber-js --tags "@smoke or @negative"
# Exclude scenarios with tag
npx cucumber-js --tags "not @wip"
# Complex expressions
npx cucumber-js --tags "(@smoke or @critical) and not @wip"
Data Tables
Data tables allow passing structured data to steps.
Using Data Tables
Feature: User Registration
Scenario: Register with valid information
Given I am on the registration page
When I fill in the registration form with:
| field | value |
| name | John Doe |
| email | john@example.com |
| password | SecurePass123! |
| confirm | SecurePass123! |
And I click the register button
Then I should see a success message
Scenario: Add multiple items to cart
Given I have an empty cart
When I add the following items:
| product | quantity | price |
| Laptop | 1 | 999.99 |
| Mouse | 2 | 29.99 |
| Keyboard | 1 | 79.99 |
Then my cart total should be 1139.96
Implementing Data Table Steps
import { When, Then, DataTable } from '@cucumber/cucumber';
When('I fill in the registration form with:', function (dataTable: DataTable) {
const data = dataTable.rowsHash();
// data = { name: 'John Doe', email: 'john@example.com', ... }
console.log('Name:', data.name);
console.log('Email:', data.email);
});
When('I add the following items:', function (dataTable: DataTable) {
const items = dataTable.hashes();
// items = [
// { product: 'Laptop', quantity: '1', price: '999.99' },
// { product: 'Mouse', quantity: '2', price: '29.99' },
// ...
// ]
items.forEach(item => {
console.log(`Adding ${item.quantity} x ${item.product} at $${item.price}`);
});
});
Doc Strings
Doc strings allow passing multi-line text to steps.
Scenario: Create a blog post
Given I am logged in as an author
When I create a new post with content:
"""
# Welcome to My Blog
This is my first blog post. I'm excited to share
my thoughts with you.
## What to Expect
- Technical tutorials
- Industry insights
- Personal experiences
"""
Then the post should be saved as draft
When('I create a new post with content:', function (content: string) {
console.log('Post content:', content);
// content is the full multi-line string
});
Exercise: Write Feature Files
Create feature files for a shopping application.
Exercise 1: Shopping Cart Feature
Write a complete feature file for shopping cart functionality:
Feature: Shopping Cart
As a customer
I want to manage items in my shopping cart
So that I can purchase products
# Write scenarios for:
# - Adding item to empty cart
# - Adding multiple items
# - Updating item quantity
# - Removing item from cart
# - Clearing the entire cart
# - Viewing cart total
Exercise 2: Checkout Feature
Write a complete feature file for checkout:
Feature: Checkout Process
As a customer with items in my cart
I want to complete the checkout process
So that I can receive my products
# Write scenarios for:
# - Successful checkout with valid payment
# - Checkout with invalid credit card
# - Applying discount code
# - Selecting shipping method
# - Guest checkout vs logged-in checkout
Exercise 3: Product Search Feature
Write a complete feature file for product search:
Feature: Product Search
As a customer
I want to search for products
So that I can find what I'm looking for
# Write scenarios for:
# - Search by product name
# - Search with no results
# - Filter search results by category
# - Sort search results by price
# - Pagination of search results
Self-Assessment
After completing this chapter, you should be able to:
- [ ] Explain the difference between BDD and TDD
- [ ] Write feature files using Gherkin syntax
- [ ] Use all Gherkin keywords appropriately
- [ ] Implement basic step definitions
- [ ] Use tags to organize scenarios
- [ ] Work with data tables and doc strings
Next Steps
Continue to Chapter II - Writing Feature Files to learn advanced Gherkin patterns and best practices.