Skip to main content

TypeScript Cucumber BDD (Tutorial, Mac)

In this article, I show you how to test a TypeScript library using the Cucumber BDD (Behavior Driven Development) framework.

What is Cucumber BDD?

Cucumber BDD (Behavior Driven Development) is an open-source software development approach that enhances communication amongst project stakeholders and streamlines the process of requirements understanding and system documentation.

Central to Cucumber BDD is the use of a simple, domain-specific language called Gherkin which allows for the creation of human-readable documentation of software behavior without detailing how that functionality is implemented.

These documentation files, written in plain language, serve as both requirements and test documentation, ensuring that all parties involved—from developers to business analysts—have a clear and shared understanding of the project objectives.

As a result, Cucumber facilitates the collaboration between technical and non-technical team members, promotes living documentation, and supports automated testing by providing a bridge between the business requirements and the technical tests.

Example Cucumber BDD feature file

Here is a simple Cucumber feature file using the Gherkin syntax. It's for a hypothetical online bookstore application. In this example, we'll define a feature for searching a book by its title.

Filename: search_book_by_title.feature

Feature: Book Search
In order to find books more easily
As a website user
I want to be able to search for books by title

Scenario: User finds a book by its title
Given the following books exist:
| Title | Author |
| 1984 | George Orwell |
| To Kill a Mockingbird | Harper Lee |
When the user searches for "1984"
Then the user should see "1984" by "George Orwell" in the search results

Scenario: User does not find a book when searching for a non-existent title
Given the following books exist:
| Title | Author |
| Brave New World | Aldous Huxley |
| The Great Gatsby | F. Scott Fitzgerald |
When the user searches for "The Catcher in the Rye"
Then the user should see a message indicating no results were found

In this feature file, we've defined two scenarios under the feature "Book Search":

  1. User finds a book by its title: This scenario tests the positive case where the user is able to find a book by its title.
  2. User does not find a book when searching for a non-existent title: This scenario handles the negative case where the user searches for a book that doesn't exist in the system, and a message should be displayed indicating no results were found.

Each scenario follows the Given-When-Then structure:

  • Given steps are used to set up the initial context (in this case, the existence of certain books).
  • When steps are used to describe an action (here, the user's search action).
  • Then steps are used to assert the expected outcome (what the user should see as the search result).

This is a basic example, but it illustrates how the Gherkin syntax is used to write human-readable tests that can be understood by all stakeholders and can also be automated by developers.


Cucumber BDD Step by Step Tutorial

Now that we have the basic ideas covered, I will walk you through how to create a simple library in TypeScript and test it using Cucumber BDD.

info

These instructions were written and tested on a Mac.

Step 1. Install Node.js and npm

To get started, you first need to create a Node.js project:

  • Open the Terminal application on your Mac

You can find it in the Applications > Utilities folder.

  • Check if you have Node.js and npm installed by typing the following commands in the Terminal:
node -v
npm -v
  • If Node.js and npm are not installed, you can download and install them from the Node.js website (https://nodejs.org/). Download the LTS version, which is recommended for most users.

Step 2. Create a project folder

In the Terminal window, run the following commands:

mkdir -p ~/projects/typescript/typescript-cucumber-bdd-demo
cd ~/projects/typescript/typescript-cucumber-bdd-demo

Step 3. Initialize the project

Run this command to initialize a new project:

npm init -y

The -y flag instructs the init command to use the defaults for creating the package.json file. I'll show you how to modify it later.

Step 4. Install TypeScript as a dependency

You can either install TypeScript globally or as a project dependency.

It is recommended that you install it as a project dependency, like this:

npm install typescript --save-dev

This command installs TypeScript and adds it to the "devDependencies" section of your package.json file.

Step 5. Create a source folder

You can't run TypeScript directly using Node.js. You have to "transpile" it to JavaScript.  The way that works is that you put your source TypeScript (*.ts) files in one folder, and define a second target distribution folder for the transpiler-generated JavaScript (*.js) files.

  • Create a new folder for your source by running this command:
mkdir src

You don't have to create the target folder, because the build command will do that for you.  I'll show you in the next step how to define the location.

Step 6. Configure TypeScript

The TypeScript transpiler (tsc) will look for a file called tsconfig.json in the root of your projects folder.

To create a simple config file that references your source and distribution files, do the following:

  • Make a new tsconfig.json file:
touch tsconfig.json
  • Edit the file, paste in the contents from this listing, and save the file:
{
"compilerOptions": {
"target": "ES6",
"module": "CommonJS",
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}

The important things to note are:

  • the rootDir path must match the location of your source folder (./src)
  • the outDir path defines the target folder for the build command to create
  • the output of the build command will be written to the outDir

Step 7. Create a source file

Create a new source file with a *.ts extension in the source src folder.

  • Run this command:
touch src/index.ts
  • Edit src/index.ts, paste in the contents from this listing, and save the file.
// Author: Mitch Allen
// File: index.ts

export function add(a: number, b: number) : number {
return a + b
}

This exports a simple function (add) that takes two numbers for arguments, and returns the result of adding them together.

Step 8. Create a build command

To build the package you need to add a script command to call tsc.

  • Edit package.json
  • Add the build script to the scripts block, like this:
  "scripts": {
"build": "tsc",
"test": "echo \"Error: no test specified\" && exit 1"
},

When the build command runs, it will transpile your code to ./dist/index.js.  So change the main line in package.json to this:

"main": "./dist/index.js",
  • Save the package.json file

Step 9. Run the build

You can now run the build with this command:

npm run build

Check the contents of the dist folder:

ls -ls dist

You should see a new file called index.js

You can view the contents of the file with this command:

cat dist/index.js

You will see the result of your TypeScript file converted (transpiled) into JavaScript.

info

The format of the JavaScript file will depend on your target setting in the tsconfig.json file. Going into details is beyond the scope of this article.

Step 10. Add a clean command

The files in the dist folder don't need to be saved or checked in.  You can delete them at any time and recreate them using the build command.  It's good to do that from time to time to remove any outdated files.

You can create a clean command by doing the following:

  • Edit package.json
  • Add a clean script command like this:
  "scripts": {
"build": "tsc",
"clean": "rm -rf dist",

You can run the clean command like this:

npm run clean

You will now see that the dist folder is gone:

ls -ls

Run the start command again to see that the dist folder is automatically recreated.

npm start

Step 11. Add the dist folder to .gitignore

Since you don't need to keep the dist files, you can add them to .gitignore with a line like this:

# Ignore built files
dist/**/*

To use that do the following:

  • Create a .gitignore file:
touch .gitignore

That file contains a line to ignore the dist folder.

Step 12. Install cucumber

Now that the project has been created, it's time to add test automation using the Cucumber BDD framework.

To install Cucumber, run this command in the root of your project:

npm install --save-dev @cucumber/cucumber ts-node 

This installs:

  • @cucumber/cucumber - the cucumber framework
  • ts-node - a JIT (Just In Time) compiler to run your TypeScript test files directly without having to transpile them to JavaScript

Step 13. Create a cucumber configuratiom file

Cucumber is made up of two types of files:

  • feature files (.feature extension) - readable steps of how something should be tested using the Gherkin syntax
  • step definition files (.ts extension - when using TypeScript) - the readable steps converted to code

By default cucumber looks for feature and step files in root project folders. But I prefer to combine them in a parent test folder.

I can pass flags to cucumber at the command line telling it where the files are. Or I can use a configuration file (cucumber.js).

I'll show you how to set that up using a config file here:

Run this command from the root of your project:

touch cucumber.js

Paste this code into cucumber.js and save the file:

// Author: Mitch Allen
// File: cucumber.js

module.exports = {
default: {
requireModule: ['ts-node/register'],
paths: ['test/features/*.feature'],
require: ['test/step-definitions/**/*.ts']
}
};

The config file defines the following:

  • requireModule - a list of modules to include, in this case the JIT compiler
  • paths - this setting tells cucumber where to look for feature files
  • require - this setting tells cucumber where to find code to intepret the matching steps

Step 14. Create a feature file and folder

From the root of the project, run this command to create a folder containing a new feature file:

mkdir -p test/features
touch test/features/add.feature

The path needs to match the paths glob pattern in the cucumber.js file that was defined previously.

Paste this into the add.feature file and save it.

Feature: Add function
Scenario: Adding two numbers
Given I have a number 5
And I have another number 3
When I add them
Then I get 8

Step 15. Create a step definition file and folder

Run this command from the root of your project:

mkdir -p test/step-definitions
touch test/step-definitions/add.steps.ts

The path needs to match the require glob pattern in the cucumber.js file that was defined previously.

Paste this into add.steps.ts and save it:

import { Given, When, Then } from '@cucumber/cucumber';
import { add } from '../../src';

function assert(condition: any, message: string = "Assertion failed"): asserts condition {
if (!condition) {
throw new Error(message);
}
}

Given('I have a number {int}', function (number: number) {
this.a = number;
});

Given('I have another number {int}', function (number: number) {
this.b = number;
});

When('I add them', function () {
this.result = add(this.a, this.b);
});

Then('I get {int}', function (expectedResult: number) {
assert( this.result == expectedResult )
});
info

It's important to note that the text in the Given / When / Then function calls need to match the steps in the feature file, or the tests won't compile and run properly.

Step 16. Add / update the test script

To run the tests, all you need to do is call cucumber-js from an npm script.

To set that up:

  • Edit package.json in the root of the project
  • In the scripts section, add or update test to run cucumber-js like this:
"test": "cucumber-js"

By default cucumber-js will look to the root of the project for the cucumber.js configuration file that we defined earlier.

Step 17. Run the tests

From the root of the project, run this command:

npm test
  • Verify that the test ran and passed without error.

The results should look something like this:

% npm test                                        

> [email protected] test
> cucumber-js

....

1 scenario (1 passed)
4 steps (4 passed)
0m00.007s (executing steps: 0m00.000s)

Congratulations! You managed to build a TypeScript libary and test it using the Cucumber BDD framework !

Example

You can find an example repo for this project here:

Conclusion

In this article, you learned:

  • What is the Cucumber BDD framework
  • How to setup a project to transpile a TypeScript function library into JavaScript
  • How to install the Cucumber BDD framework test it

Help pay for this site by picking up a nice mug!

References