BDD testing using Cucumber.js

Categories BDD, JavaScript

This guide will help you to be able to test your behaviour of your application(Behaviour Driven Development) using Cucumber.js. If you don’t know about Behaviour Driven Development, click here to read more about it.

Cucumber.js is a tool for running automated tests written in plain language(Gherkin). You can find more about Gherkin language here.

Cucumber.js is the JavaScript implementation of Cucumber and runs on Node.js and modern web browsers. At this point it is worth mentioning that Cucumber supports a number of different software platforms and Cucumber.js is one of them. Check out here how many Cucumber implementations exist.

In order to run Cucumber.js on your machine, you need to have installed Node.js and npm

Assuming that you use a Linux environment, you can simply follow the steps below to install Cucumber.js and all of these dependencies that are needed for testing a web application. For any other case or preference, please use the links that represent those packages and find more details.

Install Node.js and npm if you haven’t installed them already, following the instructions from the Node.js website here.

After you have installed successfully Node.js and npm, go to your project’s root directory and install Cucumber.js

npm install --save-dev cucumber

If you want to install Cucumber.js globally in order to run it in every project without needing install it inside each project then run the following command instead.

npm install -g cucumber

Because I need a browser to run in the background in order to load my web application then I need to install one. There are plenty, I have chosen Zombie.js

npm install zombie --save-dev

Now we are ready to write our scenarios and testing the behaviour of our application using Cucumber.js

Before start writing the test cases, we have to check if a folder with name node_modules has been created in the project’s root directory. Inside there, we can find every package has been installed through npm in order to be used for testing purpose.

As I said before, the automated tests are written in Gherkin language. The file that includes the testing scenarios of an application’s feature that need to be tested, it should have the extension feature. In my case, I want to test the authentication process of my web application so I will add one feature with name Authentication and two scenarios, one for Unsuccessful login and another one for Successful login. I will name the file as authentication.feature

The file content will be the following.

# features/authentication.feature

Feature:Authentication process
 In order to have access to the management area
 As a user
 I need to be able to login

 Scenario: Unsuccessful login
  Given I am on the "login" page
  When I submit the login form with username "wrongUsername" and password = "wrongPassword"
  Then I should see "Wrong username or password. Please try again."
  And I should be on the "login" page

 Scenario: Successful login
  Given I am on the "login" page
  When I submit the login form with username "correctUsername" and password = "correctPassword"
  Then I should see "Welcome to the Management Area."
  And I should be on the "management-area" page

All the features need to be stored under a folder with name features. Create this folder inside your application’s root directory.

Let’s go and run Cucumber.js to see what the output of this test is. If you have installed Cucumber.js globally, then you can simply run it when you are in your project’s root directory, typing in the console the command

cucumber.js

otherwise, you can run Cucumber.js inside your project from the following directory

./node_modules/.bin/cucumber.js

This command will be run all the tests that will be found in the features directory. To run a particular feature, you need to specify it.

./node_modules/.bin/cucumber.js features/authentication.feature

There are plenty of options about how you can run exact scenario inside a particular test but you can find more in the Cucumber.js documentation here.

After you have run this command, you should get back in the terminal an output with a lot of warnings about undefined tests. The console output specifies how the tests should be implemented and it gives you some snippets. In this example, the first suggested snippet should be like this below.

this.Given('I am on the {arg1:stringInDoubleQuotes} page', function(arg1, callback) {
 // Write code here that turns the phrase above into concrete actions
 callback(null, 'pending');
});

Let’s now create the tests! Inside the features folder we have to add another folder with name step_definitions. This is the directory that Cucumber.js is looking by default to find JavaScript files with the same names as features have. Of course this path can be customised if it’s needed following the instructions from the Cucumber.js documentation. So, inside the folder step_definitions, I have created a JavaScript file with name authentication and it looks like this:

// features/step_definitions/authentication.js

var {defineSupportCode} = require('cucumber');

defineSupportCode(function({Given, When, Then}) {
 Given('I am on the {arg1:stringInDoubleQuotes} page', function(arg1, callback) {
  // Write code here that turns the phrase above into concrete actions
  callback(null, 'pending');
 });

 When('I submit the login form with username = {arg1:stringInDoubleQuotes} and password = {arg2:stringInDoubleQuotes}', function(arg1, arg2, callback) {
  // Write code here that turns the phrase above into concrete actions
  callback(null, 'pending');
 });

 Then('I should see {arg1:stringInDoubleQuotes}', function(arg1, callback) {
  // Write code here that turns the phrase above into concrete actions
  callback(null, 'pending');
 });

 Then('I should be on {arg1:stringInDoubleQuotes}', function(arg1, callback) {
  // Write code here that turns the phrase above into concrete actions
  callback(null, 'pending');
 });
});

Now if you run again the tests for this feature, you will see that all the tests are pending. Now you have to write the actual code that will test your application’s behaviour. Before you start writing your tests, it’s good to know that Cucumber.js gives you the possibility of adding JavaScript support files that can be loaded before your tests run. By default, Cucumber.js is looking and loading these JavaScript files that are located inside a folder with name support and this folder should be exist under the features directory. In my case, I’m going to add a JavaScript file with name world. Inside this file I will include variable, modules and methods that I want to use inside my tests. My file includes the followings:

// features/support/world.js

var zombie = require('zombie');
var {defineSupportCode} = require('cucumber');

zombie.localhost('my-project.local', 80);

function World() {
 this.browser = new zombie();
 this.baseUrl = 'http://my-project.local';
}

defineSupportCode(function({setWorldConstructor}) {
 setWorldConstructor(World)
});

Here I pass only the browser and one variable that I want to use inside my tests but also I could pass more things like a method that I could call it in order to login as a user without needing to submit the form using credentials.

Ok, let’s finalise the tests.

// features/step_definitions/authentication.js

var {defineSupportCode} = require('cucumber');

defineSupportCode(function({Given, When, Then}) {
 Given('I am on the {arg1:stringInDoubleQuotes} page', function(arg1, callback) {
  // visit the page
  this.browser.visit(arg1, callback);
 });

 When('I submit the login form with username = {arg1:stringInDoubleQuotes} and password = {arg2:stringInDoubleQuotes}', function(arg1, arg2, callback) {
  // submit the login for
  this.browser
   .fill('username', arg1)
   .fill('password', arg2)
   .pressButton('Login', callback);
 });

 Then('I should see {arg1:stringInDoubleQuotes}', function(arg1, callback) {
  // get the text of the body
  var tbody = this.browser.text('body');

  // search if the text exists inside the body text
  if (body.search(arg1) != -1) {
   // text exists
   callback();
  } else {
   // throw an error for missing text
   callback(new Error('Expected to see the text: ' + arg1));
  }
 });

 Then('I should be on {arg1:stringInDoubleQuotes}', function(arg1, callback) {
  // check if browser url is correct
  this.browser.assert.url(this.baseUrl + arg1);
  
  callback();
 });
});

With the above code, the tests should pass. Play around, change the scenarios and see when the tests fail or pass.

The structure of the folders into your project should be the following.

features
 |- step_definitions
    |- authentication.js
 |- support
    |- world.js
 |- authentication.feature
In conclusion

The above Cucumber.js example is all about a demonstration that shows how the behaviour of a particular functionality of a web application can be tested.

Based on the above codebase, always you can use additional node modules to make your tests more efficient for any case.