Building Enterprise JavaScript Applications
上QQ阅读APP看书,第一时间看更新

Laying out our step definitions

With the help of Gherkin, we now have our specification written in plain English. Next, let's try to use Cucumber to run our specification.

By default, Cucumber will look for a directory called features in the project's root directory and run the .feature files it finds inside. Since we have placed our main.feature file inside the spec/cucumber/features directory, we should pass this path to Cucumber:

$ npx cucumber-js spec/cucumber/features
UUUUUU

Warnings:
1) Scenario: Empty Payload
? When the client creates a POST request to /users
Undefined.
? And attaches a generic empty payload
Undefined.
? And sends the request
Undefined.
? Then our API should respond with a 400 HTTP status code
Undefined.
? And the payload of the response should be a JSON object
Undefined.
? And contains a message property which says "Payload should not be
empty"
Undefined.

1 scenario (1 undefined)
6 steps (6 undefined)

The test result informs us that our tests are undefined. This is because Cucumber is not clever enough to parse the plain text specification and figure out how to run these tests. We must link these steps to actual JavaScript code, which, in the context of Cucumber, are called step definitions.

Create a new directory called steps next to the features directory; here's where we'll define all our step definitions:

$ mkdir -p spec/cucumber/steps

Defining steps inside their own directory helps us to mentally dissociate steps from being tied to any particular feature, and keep steps as modular as possible. Create an index.js file within the steps directory and add the following placeholder step definitions:

import { When, Then } from 'cucumber';

When('the client creates a POST request to /users', function (callback) {
callback(null, 'pending');
});

When('attaches a generic empty payload', function (callback) {
callback(null, 'pending');
});

When('sends the request', function (callback) {
callback(null, 'pending');
});

Then('our API should respond with a 400 HTTP status code', function (callback) {
callback(null, 'pending');
});

Then('the payload of the response should be a JSON object', function (callback) {
callback(null, 'pending');
});

Then('contains a message property which says "Payload should not be empty"', function (callback) {
callback(null, 'pending');
});
If you have the ESLint extension installed on your editor, you may see ESLint complain about arrow functions and function names. Normally, these would be valid problems, but this is not the case in our test files. Therefore, we should override the default configuration and turn these rules off.

Inside the spec/ directory, create a new .eslintrc.json file, and paste in the following content:

{
"rules": {
"func-names": "off",
"prefer-arrow-callback": "off"
}
}
This will turn off the  func-names and  prefer-arrow-callback rules for all files inside the spec/ directory.

Each step definition consists of the step keyword method (When/Then and so on), which takes in two parameters. The first one is the pattern, which is a string that is used to match the text in the feature specification with the step definition. The second parameter is the code function, which is a function that is run for that step.

In our example, when Cucumber gets to the When the client creates a POST request to /users step in our scenario, it will try to run the function associated with the When('the client creates a POST request to /users') step definition, because the pattern matches the step description.