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');
});
Inside the spec/ directory, create a new .eslintrc.json file, and paste in the following content:
{This will turn off the func-names and prefer-arrow-callback rules for all files inside the spec/ directory.
"rules": {
"func-names": "off",
"prefer-arrow-callback": "off"
}
}
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.