Cucumber.js with TypeScript
Tags: testing, typescript, node, cucumber
Published: 2019-05-30 13:36:04

---
title: Cucumber.js with TypeScript
description: Get a basic Cucumber.js test suite running with TypeScript and cucumer-tsflow
published: true
tags: testing, typescript, node, cucumber
canonical_url: https://elliotdenolf.com/posts/cucumberjs-with-typescript/
---

> Originally posted on my blog at https://elliotdenolf.com/posts/cucumberjs-with-typescript

Cucumber.js is the JavaScript implementation of [Cucumber](https://cucumber.io/). The main benefit of writing automated tests for Cucumber is that they are written in plain English, so any non-technical person can read the scenarios and know what is being tested. This is extremely powerful in larger organizations because it allows developers, testers, and business stakeholders to better communicate and collaborate.

This post will go through setting up a basic Cucumber.js suite using TypeScript and [cucumber-tsflow](https://www.npmjs.com/package/cucumber-tsflow). Cucumber-tsflow is a package that will allow us to take advantage of TypeScript’s [decorators](https://www.typescriptlang.org/docs/handbook/decorators.html), which make for clearer step definition code.

The first step will be installing our dependencies:

```sh
npm i -D cucumber cucumber-tsflow cucumber-pretty ts-node typescript chai
npm i -D @types/cucumber @types/chai
```

`experimentalDecorators` must also be set to `true` in your `tsconfig.json` in order for the decorators to compile properly.

The two main components for cucumber tests are feature files and step definitions. Let’s start out by creating a `features` directory then creating a file named `bank-account.feature` inside it. Our example will be testing the basic functionality of a bank account.

```feature
# features/bank-account.feature
Feature: Bank Account

  Scenario: Stores money
    Given A bank account with starting balance of $100
    When $100 is deposited
    Then The bank account balance should be $200
```

This defines a single scenario for depositing money into a bank account. Next, we will create a directory named `step-definitions` and create a file named `bank-account.steps.ts` within it.

```typescript
import { binding, given, then, when} from 'cucumber-tsflow';
import { assert } from 'chai';

@binding()
export class BankAccountSteps {
  private accountBalance: number = 0;

  @given(/A bank account with starting balance of \$(\d*)/)
  public givenAnAccountWithStartingBalance(amount: number) {
    this.accountBalance = amount;
  }

  @when(/\$(\d*) is deposited/)
  public deposit(amount: number) {
    this.accountBalance = Number(this.accountBalance) + Number(amount);
  }

  @then(/The bank account balance should be \$(\d*)/)
  public accountBalanceShouldEqual(expectedAmount: number) {
    assert.equal(this.accountBalance, expectedAmount);
  }
}
```

We are utilizing the [cucumber-tsflow](https://www.npmjs.com/package/cucumber-tsflow) package which exposes some very useful [decorators](https://www.typescriptlang.org/docs/handbook/decorators.html) for our `Given`, `When`, and `Then` steps. The code within each step is fairly simple. The `Given` step initializes the `accountBalance`, the `When` step adds to the balance, and the `Then` step asserts its value.

Some specific things to note: this file exports a single class which has the `@binding()` decorator on it which is required for cucumber-tsflow to pick up the steps. Each step definition must also have a `@given`, `@when` or `@then` decorator on it. These decorators take a regular expression as a parameter which is how the lines in the feature file map to the code. Also, make note that there are capture groups in the expressions to capture values from the text and are subsequently passed as parameters to the function.

Cucumber is run using the `cucumber-js` command with a series of command-line switches. However, this can optionally be put into a `cucumber.js` file at the root of the project. Create a `cucumber.js` file at the root of the project with the following contents:

```js
// cucumber.js
let common = [
  'features/**/*.feature', // Specify our feature files
  '--require-module ts-node/register', // Load TypeScript module
  '--require step-definitions/**/*.ts', // Load step definitions
  '--format progress-bar', // Load custom formatter
  '--format node_modules/cucumber-pretty' // Load custom formatter
].join(' ');

module.exports = {
  default: common
};
```

Putting the configuration in this file allows us to simply pass the profile name to `cucumber-js` (`default` in our case) instead of a long list of arguments. This file is building out all of the command line arguments, joining them, then exporting them under a named property. Let’s add an npm script to our `package.json`, so we can easily run it.

```json
// package.json
{
  // ...
  "scripts": {
    "test": "./node_modules/.bin/cucumber-js -p default"
  },
  // ...
}
```

The structure of your project should now look like this:

```
.
|-- cucumber.js
|-- features
| `-- bank-account.feature
|-- package.json
|-- step-definitions
| `-- bank-account.steps.ts
`-- tsconfig.json
```

Now when we run `npm test`, `cucumber-js` inside of our `node_modules` will be executed with the `-p default` switch denoting the default profile exported from our `cucumber.js` file we created earlier.

The output should be something similar to this:

```feature
Feature: Bank Account

  Scenario: Stores money
    Given A bank account with starting balance of $100
    When $100 is deposited
    Then The bank account balance should be $200

1 scenario (1 passed)
3 steps (3 passed)
0m00.004s
```

That’s it! You’re up and going with Cucumber and TypeScript!

### <svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>Links

- **[Source code for this example](https://github.com/denolfe/blog-code-samples/tree/master/posts/cucumber-with-typescript)**
- [Cucumber](https://cucumber.io/)
- [cucumber-tsflow](https://www.npmjs.com/package/cucumber-tsflow)
- [Feature file syntax reference (Gherkin)](https://docs.cucumber.io/gherkin/reference/)