CUCUMBERJS Tutorial Course
Introduction to Cucumber.js
What is Cucumber.js?
Cucumber.js is a JavaScript framework for behavior-driven development (BDD) that allows developers to write tests in a human-readable format. It supports writing scenarios in Gherkin language, which describes the expected behavior of an application in plain English. This makes it easier for non-technical stakeholders to understand the tests and for developers to ensure that the application behaves as expected.Installing Cucumber.js
1mkdir cucumber-js-project
2cd cucumber-js-project
3npm init -y
4npm install --save-dev @cucumber/cucumber
5npm install selenium-webdriver chai --save-dev
Getting Started with Cucumber.js
Setting Up Feature Files
1mkdir features
2mkdir features/step_definitions
3touch features/login.feature
1Feature: User Login
2
3 Scenario: Successful login with valid credentials
4 Given the user is on the login page
5 When the user enters a valid username and password
6 And the user clicks the login button
7 Then the user should be redirected to the dashboard
Writing Step Definitions
Inside the features/step_definitions directory, create a JavaScript file for your step definitions.touch features/step_definitions/loginSteps.js
1const { Given, When, Then } = require('@cucumber/cucumber');
2const { expect } = require('chai');
3const { Builder, By } = require('selenium-webdriver');
4
5let driver;
6
7Given('the user is on the login page', async function() {
8 driver = new Builder().forBrowser('chrome').build();
9 await driver.get('https://example.com/login');
10});
11
12When('the user enters a valid username and password', async function() {
13 await driver.findElement(By.id('username')).sendKeys('validUser');
14 await driver.findElement(By.id('password')).sendKeys('validPassword');
15});
16
17When('the user clicks the login button', async function() {
18 await driver.findElement(By.id('loginButton')).click();
19});
20
21Then('the user should be redirected to the dashboard', async function() {
22 const url = await driver.getCurrentUrl();
23 expect(url).to.equal('https://example.com/dashboard');
24 await driver.quit();
25});
1"scripts": {
2 "test": "cucumber-js"
3}
npm test
Understanding Gherkin Syntax
Gherkin Keywords: Given, When, Then
The Given keyword is used to describe the initial context of the system. It sets up the preconditions or the initial state before the main actions occur. The When keyword describes an action or event. It represents the main event that triggers some behavior in the system.The Then keyword is used to describe the expected outcome or result. It checks whether the system behaves as expected after the action.1Feature: User Login
2
3 Scenario: Successful login with valid credentials
4 Given the user is on the login page
5 When the user enters a valid username and password
6 And the user clicks the login button
7 Then the user should be redirected to the dashboard
Using Backgrounds in Gherkin
In Gherkin, the Background keyword is used to define a set of steps that are common to all scenarios in a feature file. This is especially useful when you have multiple scenarios that share the same setup steps. By using a Background, you can avoid repeating the same Given steps in each scenario, making your feature files more concise and easier to maintain.1Feature: User Authentication
2
3 Background:
4 Given the user is on the login page
5 And the user has a valid username and password
6
7 Scenario: Successful login
8 When the user enters the username and password
9 And the user clicks the login button
10 Then the user should be redirected to the dashboard
11
12 Scenario: Failed login with incorrect password
13 When the user enters the username and an incorrect password
14 And the user clicks the login button
15 Then the user should see an error message
16
17 Scenario: Login with empty credentials
18 When the user leaves the username and password fields empty
19 And the user clicks the login button
20 Then the user should see a validation message
Scenario Outlines with Examples
In Gherkin, Scenario Outline is used to run the same scenario multiple times with different sets of data. This is particularly useful when you want to test the same behavior with various input values. The Scenario Outline works together with the Examples keyword, which provides the different sets of inputs and expected outcomes for each iteration of the scenario.1Feature: User Login
2
3 Scenario Outline: Login with different types of credentials
4 Given the user is on the login page
5 When the user enters the username "<username>"
6 And the user enters the password "<password>"
7 And the user clicks the login button
8 Then the user should see the message "<message>"
9
10 Examples: Valid Credentials
11 | username | password | message |
12 | validUser1 | validPass1 | Welcome, validUser1! |
13 | validUser2 | validPass2 | Welcome, validUser2! |
14
15 Examples: Invalid Credentials
16 | username | password | message |
17 | invalidUser | validPass | Incorrect username or password. |
18 | validUser | invalidPass| Incorrect username or password. |
Running Cucumber.js Tests
Cucumber.js CLI Options
1cucumber-js --require ./features/step_definitions/
2cucumber-js --tags "not @skip and @important"
3npx cucumber-js --tags "@login or @registration"
4npx cucumber-js --tags "@smoke and not @slow"
5npx cucumber-js --tags "@login and @smoke"
6
7cucumber-js --format json
8cucumber-js --format-options '{"output": "report.json"}'
9
10//Runs scenarios that match a given name or regex pattern.
11cucumber-js --name "Login"
12
13//Fails the test run if there are any undefined or pending steps.
14cucumber-js --strict
15
16//Passes parameters to the World constructor. This is useful for passing configuration settings to your tests.
17cucumber-js --world-parameters '{"baseUrl": "http://localhost"}'
18
19//Retries failed scenarios up to n times
20cucumber-js --retry 2
21
22//Stops the test execution immediately after the first failure.
23cucumber-js --fail-fast
24
25//parallel execution
26cucumber-js --parallel 4
27
28//Validates the configuration, and step definitions without actually running the steps.
29cucumber-js --dry-run
30
31//Specifies the syntax of the step definition snippets generated for undefined steps.
32cucumber-js --snippet-syntax async
Parallel Test Execution
Parallel test execution in Cucumber.js allows you to run multiple scenarios or feature files concurrently, significantly reducing the total execution time of your test suite. Cucumber.js supports parallel execution out of the box, which can be configured easily through the command-line interface (CLI).cucumber-js --parallel 4
Assertions
NodeJS Assert
1const assert = require('assert');
2
3// Check if two values are strictly equal
4const actualValue = 5;
5const expectedValue = 5;
6assert.strictEqual(actualValue, expectedValue, 'Values should be strictly equal');
7
8// Check if two values are loosely equal
9assert.equal(actualValue, expectedValue, 'Values should be loosely equal');
10
11
12// Check if two objects are deeply equal
13const actualObject = { name: 'Alice', age: 30 };
14const expectedObject = { name: 'Alice', age: 30 };
15assert.deepStrictEqual(actualObject, expectedObject, 'Objects should be deeply equal');
16
17
18// Check if a value is true
19const isTrue = true;
20assert.ok(isTrue, 'Value should be true');
21
22// Check if a value is false
23const isFalse = false;
24assert.fail(isFalse, 'Value should be false');
25
26
27// Function that should throw an error
28function throwsError() {
29 throw new Error('This is an error');
30}
31
32// Check if function throws an error
33assert.throws(() => {
34 throwsError();
35}, Error, 'Function should throw an error');
36
37
38// Function that should throw an error with a specific message
39function throwsSpecificError() {
40 throw new Error('Specific error message');
41}
42
43// Check if function throws an error with specific message
44assert.throws(() => {
45 throwsSpecificError();
46}, {
47 name: 'Error',
48 message: 'Specific error message'
49}, 'Function should throw an error with the specific message');
50
51
52// Function that should not throw an error
53function doesNotThrowError() {
54 return 'No error';
55}
56
57// Check if function does not throw an error
58assert.doesNotThrow(() => {
59 doesNotThrowError();
60}, 'Function should not throw an error');
61
62// Check if value is of a specific type
63const value = 'Hello';
64assert(typeof value === 'string', 'Value should be of type string');
65
66
67// Check if two values are equal with a custom error message
68const actual = 10;
69const expected = 20;
70assert.strictEqual(actual, expected, `Expected ${expected} but got ${actual}`);
Chai Integration
1//npm install chai
2
3const assert = require('chai').assert;
4
5// 1. Basic Equality Assertion
6const actualValue = 5;
7const expectedValue = 5;
8assert.equal(actualValue, expectedValue, 'Values should be equal');
9
10// 2. Deep Equality Assertion
11const actualObject = { name: 'Alice', age: 30 };
12const expectedObject = { name: 'Alice', age: 30 };
13assert.deepEqual(actualObject, expectedObject, 'Objects should be deeply equal');
14
15// 3. Assertion for True/False
16const isTrue = true;
17const isFalse = false;
18assert.isTrue(isTrue, 'Value should be true');
19assert.isFalse(isFalse, 'Value should be false');
20
21// 4. Assertion for Type of Value
22const value = 'Hello';
23assert.isString(value, 'Value should be of type string');
24
25// 5. Assertion for Array and Length
26const array = [1, 2, 3];
27assert.isArray(array, 'Value should be an array');
28assert.lengthOf(array, 3, 'Array should have a length of 3');
29
30// 6. Assertion for Errors
31function throwsError() {
32 throw new Error('This is an error');
33}
34
35assert.throws(() => {
36 throwsError();
37}, Error, 'Function should throw an error');
38
39// 7. Assertion for Specific Error Message
40function throwsSpecificError() {
41 throw new Error('Specific error message');
42}
43
44assert.throws(() => {
45 throwsSpecificError();
46}, Error, 'Specific error message');
47
48// 8. Assertion for Containing Value
49const string = 'Hello, world!';
50assert.include(string, 'world', 'String should contain "world"');
51
52// 9. Assertion for Matching Pattern
53const email = '[email protected]';
54assert.match(email, /^[^s@]+@[^s@]+.[^s@]+$/, 'Email should match the pattern');
55
56// 10. Custom Assertion Error Messages
57const actual = 10;
58const expected = 20;
59assert.equal(actual, expected, `Expected ${expected} but got ${actual}`);
Using Data Tables
Handling Data Tables in Step Definitions
1Feature: User Management
2
3 Scenario: Add multiple users
4 Given the following users exist:
5 | name | email | age |
6 | Alice | [email protected] | 30 |
7 | Bob | [email protected] | 25 |
8 | Charlie | [email protected] | 35 |
9 When I retrieve the list of users
10 Then the list should contain the following users:
11 | name | email | age |
12 | Alice | [email protected] | 30 |
13 | Bob | [email protected] | 25 |
14 | Charlie | [email protected] | 35 |
1const { Given, When, Then } = require('@cucumber/cucumber');
2
3// Step definition to handle the data table of existing users
4Given('the following users exist:', function (dataTable) {
5 // Convert the data table to an array of objects
6 const users = dataTable.hashes();
7
8 // Process each user (e.g., add to a database or in-memory store)
9 users.forEach(user => {
10 console.log(`User: ${user.name}, Email: ${user.email}, Age: ${user.age}`);
11 // You can implement code to add these users to your system here
12 });
13});
14
15// Step definition to handle the data table for expected results
16Then('the list should contain the following users:', function (dataTable) {
17 // Convert the data table to an array of objects
18 const expectedUsers = dataTable.hashes();
19
20 // Fetch the actual list of users (e.g., from a database or API)
21 const actualUsers = [
22 // This should be replaced with the actual method to fetch users
23 ];
24
25 // Perform assertions to check if the actual users match the expected ones
26 assert.deepEqual(actualUsers, expectedUsers, 'The user lists do not match');
27});
Data Tables in Scenario Outlines
1Feature: User Registration
2
3 Scenario Outline: Register a user with different sets of data
4 Given I am on the registration page
5 When I fill in the registration form with the following details:
6 | name | email | password |
7 | <name> | <email> | <password> |
8 And I submit the registration form
9 Then I should see a confirmation message
10
11 Examples:
12 | name | email | password |
13 | Alice | [email protected] | password1 |
14 | Bob | [email protected] | password2 |
15 | Charlie | [email protected] | password3 |
1const { Given, When, Then } = require('@cucumber/cucumber');
2const assert = require('assert');
3
4// Mock for storing registration results
5let registrationResult = {};
6
7// Step definition for navigating to the registration page
8Given('I am on the registration page', function () {
9 // Code to navigate to the registration page
10});
11
12// Step definition for filling in the registration form with data from the table
13When('I fill in the registration form with the following details:', function (dataTable) {
14 const data = dataTable.rowsHash(); // Convert the data table to a hash map
15 const { name, email, password } = data;
16
17 // Mock registration process (replace this with actual code to interact with the form)
18 registrationResult = { name, email, password };
19});
20
21// Step definition for submitting the registration form
22When('I submit the registration form', function () {
23 // Code to submit the registration form
24 // Mock confirmation message (replace this with actual confirmation check)
25 registrationResult.confirmationMessage = `User ${registrationResult.name} registered successfully`;
26});
27
28// Step definition for verifying the confirmation message
29Then('I should see a confirmation message', function () {
30 // Mock expected confirmation message
31 const expectedMessage = `User ${registrationResult.name} registered successfully`;
32
33 // Assert that the actual confirmation message matches the expected message
34 assert.strictEqual(registrationResult.confirmationMessage, expectedMessage, 'Confirmation message does not match');
35});
Cucumber.js Hooks
Using Before and After Hooks
1Feature: Example Feature
2
3 Scenario: Simple scenario
4 Given I have a sample setup
5 When I perform an action
6 Then I should get the expected result
1const { Given, When, Then, Before, After } = require('@cucumber/cucumber');
2const assert = require('assert');
3
4// Define a global variable to be used in the hooks and steps
5let globalData = {};
6
7// Before hook - runs before each scenario
8Before(function () {
9 console.log('Running Before hook');
10 // Initialize global data or set up necessary conditions
11 globalData = { setup: true };
12});
13
14// After hook - runs after each scenario
15After(function () {
16 console.log('Running After hook');
17 // Clean up resources, close connections, etc.
18 globalData = {};
19});
20
21// Step definition for setting up
22Given('I have a sample setup', function () {
23 // Use the global data initialized in the Before hook
24 assert.strictEqual(globalData.setup, true, 'Setup was not initialized correctly');
25});
26
27// Step definition for performing an action
28When('I perform an action', function () {
29 // Perform some action; this example does not use globalData
30});
31
32// Step definition for checking the result
33Then('I should get the expected result', function () {
34 // Check the result of the action performed; this example does not use globalData
35 assert.strictEqual(true, true); // Example assertion
36});
Tagged Hooks
1@setup
2Feature: Example Feature with Setup
3
4 @important
5 Scenario: Important Scenario
6 Given I have a sample setup
7 When I perform an action
8 Then I should get the expected result
9
10 Scenario: Another Scenario
11 Given I have a sample setup
12 When I perform an action
13 Then I should get the expected result
1const { Before, After, BeforeAll, AfterAll } = require('@cucumber/cucumber');
2
3// Before hook that runs only for scenarios tagged with @important
4Before({ tags: '@important' }, function () {
5 console.log('Running Before hook for @important tagged scenarios');
6 // Setup code specific to important scenarios
7});
8
9// After hook that runs only for scenarios tagged with @important
10After({ tags: '@important' }, function () {
11 console.log('Running After hook for @important tagged scenarios');
12 // Teardown code specific to important scenarios
13});
14
15// Before hook that runs only for scenarios tagged with @setup
16Before({ tags: '@setup' }, function () {
17 console.log('Running Before hook for @setup tagged features');
18 // Setup code specific to features with @setup tag
19});
20
21// After hook that runs only for scenarios tagged with @setup
22After({ tags: '@setup' }, function () {
23 console.log('Running After hook for @setup tagged features');
24 // Teardown code specific to features with @setup tag
25});
Generating Test Reports
Generating JSON Reports
npx cucumber-js --format json:./reports/results.json
Generating HTML Reports
npm install cucumber-html-reporter --save-dev
1const reporter = require('cucumber-html-reporter');
2
3const options = {
4 theme: 'bootstrap',
5 jsonFile: './reports/results.json',
6 output: './reports/cucumber-report.html',
7 reportSuiteAsScenarios: true,
8 launchReport: true,
9 metadata: {
10 browser: {
11 name: 'chrome',
12 version: '91'
13 },
14 device: 'Local test machine',
15 platform: {
16 name: 'Windows',
17 version: '10'
18 }
19 }
20};
21
22reporter.generate(options);
node generate-report.js
Generating Allure Reports
JSON reports can be converted to HTML report using Allure command line.1npm install allure-cucumberjs --save-dev
2
3npx allure-commandline generate ./reports/allure-results --clean
4npx allure-commandline open ./reports/allure-results
Integrating Cucumber.js with CI/CD
Setting Up Cucumber.js in CI/CD
Install dependencies1npm install @cucumber/cucumber --save-dev
2npm install cucumber-html-reporter --save-dev
1{
2 "default": {
3 "require": ["./features/step_definitions/**/*.js"],
4 "format": ["json:./reports/results.json"],
5 "publishQuiet": true
6 }
7}
1"scripts": {
2 "test": "cucumber-js",
3 "report": "node generate-report.js"
4}
Running Cucumber.js Tests in CI/CD
Create a .github/workflows/ci.yml file in your repository.1name: CI
2
3on:
4 push:
5 branches:
6 - main
7
8jobs:
9 build:
10 runs-on: ubuntu-latest
11
12 steps:
13 - name: Checkout code
14 uses: actions/checkout@v2
15
16 - name: Set up Node.js
17 uses: actions/setup-node@v2
18 with:
19 node-version: '14'
20
21 - name: Install dependencies
22 run: npm install
23
24 - name: Run tests
25 run: npm test
26
27 - name: Generate report
28 run: npm run report
29
30 - name: Upload test results
31 uses: actions/upload-artifact@v2
32 with:
33 name: cucumber-report
34 path: ./reports/cucumber-report.html