Unit Testing with Mocha and Chai followed by Test Driven Development (TDD)

Introduction

Test Driven Development (TDD) is one of the major key of Agile development techniques. TDD concept can increase the quality of code, faster the development process, helps to enhance programmer confidence and improve bug detection.

Eariler it was very hard to automate web applications due to lack of testing approaches and supportive tools and frameworks hence developers relied on manual testing only which was so tedious and lenghty process. But now it can be possible to test an entire standalone services and REST API by TDD. Even rich User Interface can also be tested automatically with the help of headless browsers and other most usable browsers as well.

Well, while talking about TDD one more concept which is striking on our mind that is Behaviour Driven Development or in short BDD. BDD is based on TDD, it differs from TDD in language, which basically encourages collaboration between product owners and programmers.

In Software Development Life Cycle (SDLC) Testing is one of the major steps that has to be done before the end product is going to be deployed and available for use. A various kind of testing is being applied on the product to achieve the quality and confidence, like Unit Testing, Integration Testing, Smoke, Sanity and Regression Testing etc. To achive this Sofware Engineers need some framework which can maintain the test suites, run the test scripts and populate the test results.

Mocha  is one of the widely used JavaScript test framework which running on Node.js and in the browser. It’s making asynchronous testing simpler. It’s providing accurate test reports and also provides the stack trace of any uncaught exceptions. In other hand Chai is an assertion library which can be paired with any JavaScript testing frameworks.

Source code:   

In this entire article whatever examples we are going to discuss that is available on GitHub repository. Please clone the repository link and follow the below instructions to get a better understanding.

Agenda:  

In this article we will discuss on the below topics.

  • Setup Mocha
  • Understanding a TDD based basic mocha spec
  • Understanding the actual code execution with mocha
  • Brief discussion on Hooks
  • Managing test suite in mocha
  • Getting started with Chai
  • Testing Promises with Chai
  • Mocha Reporter

Prerequisite:  

Before starting with mocha make sure that your machine should have node and npm installed.

You can install node LTS version from nodejs website and npm will install automatically with node.

To check the versions of installed node and npm, run the below commands.

               npm -v                                // It will return installed npm version

               node -v                               // It will return installed node version

Setup Mocha:  

To install mocha run the below two commands sequentially.

                npm install – -global mocha

                npm install – -save-dev mocha 

  • – -global helps to install mocha at global level in your system, that means mocha test can be run from command prompt
  • – -save-dev helps to add mocha as a dependency in package.json file for the perticular project.

Note: Mocha is compatible with Node.js v4 or newer.

Understanding a TDD based basic mocha spec

var assert = require('assert');
let employee = ['Robart','Shyam','Abhishek','April'];

describe('Basic Mocha spec to check length and Value of an employee array', function(){
    it('Should return the size of the employee array as 4',function(){
            assert.equal(employee.length,4);
    })
    it('Should return the first name of the employee array as Robart',function(){
        assert.equal(employee[0],'Robart');
})
})

  • assert :  ‘assert’ helps to determine the test status. It determines the test failure.  
  • describe :  ‘describe’ is a function which holds the collection of tests or we could sat test suite. It’s having two parameters, first one is the meaningful name which describe the functionality under the method, and second on is the function which basically contains one or multiple tests.  A nested ‘describe’ can also be declared.
  • it :  ‘it’ is a function, which holds the actual test or test steps that needs to be executed. It’s having two parameters as well, first one is the meaningful name to the test and second one is the function which contains the body or steps of the test.

Step to execute the scripts mentioned below:

  • Download or Clone the project from gitHub
  • Navigate into the project folder via command prompt
  • Run command ‘npm install ‘ to download all the require dependencies
  • Run command ‘npm test’  to run all the test scripts or specs

Note: commands called ‘mocha test’ or only ‘mocha’ can also be use to run the test specs. Those commands along with ‘npm test’ is working as below script is mentioned under ‘package.json’ file.

"scripts": {
    "test": "mocha"
  },

Once the execution is completed below output displays on the console.

Console Output

Understanding the actual code execution with mocha

In this section we will discuss about actual code execution with mocha. Where we will see how mocha can perform test execution for various kind of functions in JavaScript.

  • Testing a function with mocha :  Every function holds some specific instructions to complete a specific tasks. To test those task, defined function needs to be call from mocha specs along with required input parameters if applicable.

Snapshot 1, the function which needs to be tested

var isEmployeePresent = function(employeeName){
    let employeePresentFlag = false;
    let listOfEmployee = ['Robart','Shyam','Abhishek','April'];

    if(listOfEmployee.indexOf(employeeName)>=0){
        return employeePresentFlag = true;
    }

    return employeePresentFlag;

}

module.exports = isEmployeePresent;

Snapshot 2, mocha test spec to test the function

var assert = require('assert');
var isEmployeePresent = require('../TestFunction/Index');


describe('Validate employee is present on the list', function(){
    it('Should return true if employee is present',function(){
           let isPresent = isEmployeePresent('April');
           assert.equal(isPresent,true);

    })
})
  • Testing Asynchronus function using mocha :  The basic difference of asynchronus function testing with mocha is that we need tell mocha once the test is completed because of the async nature of the function under test. So, we need to define a callback (i.e. ‘done’) to ‘it()’.

In the below example we can see mocha waits for ‘done()’ function to be called to complete the test.

Asynchronous function which needs to be tested


let listOfEmployee = ['Robart','Shyam','Abhishek','April'];
function isEmployeePresent(employeeName){
    return new Promise((resolve)=>{
        setTimeout(function(){
            resolve(listOfEmployee.indexOf(employeeName)>=0)
        },1000)
    })
}


module.exports = isEmployeePresent;

Mocha specs to test asynchronus function


var assert = require("assert");
var {isEmployeePresent,listLength} = require("../AsynchronusFunction/index");


describe('Validate Asynchronus function',function(){
    it('Should return true if employee name is present',function(done){
        isEmployeePresent('Abhishek',
            function(isPresent){
            assert.equal(isPresent,true);
            done();
        })
    })
    it('Should return employee list length as 4', function(){
        assert.equal(listLength(),4);
    })
})

Hooks

  • We might need to execute few of our code before or after of the actual business logic validation, in such cases we can invoke mocha’s hooks like ‘before()’, ‘after()’, ‘beforeEach()’, ‘afterEach()’ which are by default BDD – style interfaces.
  • Hooks can be use to set up preconditions and clean up after the test execution
  • It describes each test steps with valid step definations which is helpful to identify errors quickly.
  • Hooks will run in ther order they are defined.
  • Steps defined under ‘before()’ will execute once
  •  Steps defined under ‘beforeEach()’ will execute before each and every time when a new test step is about to start executing.
  • Steps defined under ‘afterEach()’ will execute after each and every time when a test step is completes execution.
  • Steps defined under ‘after()’ will execute once

Below example will demonstrate the mocha specs using mocha hooks.


var assert = require('assert');
var isEmployeePresent = require('../Hooks/hooks');
let count = 0;
describe('hooks steps', function() {
    before(function() {
      console.log("Start Test");
    });
  
    after(function() {
        console.log("End Test");
    });
  
    beforeEach(function() {
      
      console.log("Currently checking for: "+count);
    });
    describe('Validate employee is present on the list', function(){
        it('Should return true if employee is present',function(){
               let isPresent = isEmployeePresent('Robart');
               assert.equal(isPresent,true);
    
        });
        it.skip('Should return true if employee is present',function(){
            let isPresent = isEmployeePresent('Shyam');
            assert.equal(isPresent,true);
 
     });
     it('Should return true if employee is present',function(){
        let isPresent = isEmployeePresent('Abhishek');
        assert.equal(isPresent,true);

    });
    it('Should return true if employee is present',function(){
    let isPresent = isEmployeePresent('April');
    assert.equal(isPresent,true);

    })
    })
  
 afterEach(function() {
        ++count;
      console.log("Now checking for: "+count);
    });
  
  });

Output of the above spec looks like below

Console Output

Managing test suite in mocha

In this section we will discuss about test suite management in mocha. Sometime it may require to run only few specific test specs or suite instead of running all at a time or we may want to skip some specific test spec or suites during execution, that can be achieved in mocha. Mocha has one exclusive feature called ‘only()’ which basically a function which allows user to specify a suite or test which they wants to test among other . That means instead of running entire test suites or entire test within a suite user can specify which particular test suite or test cases they want to execute by using this ‘only()’ function.

Below example will demonstrate the use of ‘only()


var assert = require('assert');
var isEmployeePresent = require('../Hooks/hooks');

describe.only('Validate first two employees are present on the list', function(){
    it('Should return true if employee is present',function(){
           let isPresent = isEmployeePresent('Robart');
           assert.equal(isPresent,true);

    });
    it('Should return true if employee is present',function(){
        let isPresent = isEmployeePresent('Shyam');
        assert.equal(isPresent,true);
    });
 
})

describe('Validate Last two employees are present on the list', function(){
  
 it.only('Should return true if employee is present',function(){
    let isPresent = isEmployeePresent('Abhishek');
    assert.equal(isPresent,true);

});
it('Should return true if employee is present',function(){
let isPresent = isEmployeePresent('April');
assert.equal(isPresent,true);

})
})

Output of the above mocha specs shows up like below.

Console Output

Note: From the output we can see mocha consider only those test or suite which has been enabled by ‘only()’ .

Like ‘only()’ mocha has another inclusive feature called ‘skip()’, which is the inverse of ‘only()’. Instead of including test suites or individual test it simply tells to ignore that specific test suite or test case where ‘skip()’ function is appended. Below example will demonstrate the use of ‘skip()’


var assert = require('assert');
var isEmployeePresent = require('../Hooks/hooks');

describe('Validate first two employees are present on the list', function(){
    it('Should return true if employee is present',function(){
           let isPresent = isEmployeePresent('Robart');
           assert.equal(isPresent,true);

    });
    it.skip('Should return true if employee is present',function(){
        let isPresent = isEmployeePresent('Shyam');
        assert.equal(isPresent,true);
    });
 
})

describe('Validate Last two employees are present on the list', function(){
  
 it('Should return true if employee is present',function(){
    let isPresent = isEmployeePresent('Abhishek');
    assert.equal(isPresent,true);

});
it.skip('Should return true if employee is present',function(){
let isPresent = isEmployeePresent('April');
assert.equal(isPresent,true);

})
})

Output of the above mocha specs shows up like below.

Console Output

Note: From the output we can see mocha ignored those test or suite where ‘skip()’ is appended

Getting started with Chai

At the introduction we have got a sight of Chai assertion library. Here in this section we will talk about several interfaces of Chai, which are ‘Should’, ‘Expect’ and ‘Assert’. Chai provides an expressive and readable style of writing tets followed by BDD, which is really helpful to understand the test flow and actual validation point. Let’s understand through examples.

Installation : Before started with the examples Chai needs to be installed into the system. So to install the chai run the below command on command prompt.

npm install chai”   // This command will install chai in the project and add as a dependency

Assertion Style:  As per the Chai documentation assertion style divided into two category.

Assert:

  • Exposed through ‘assert’ interface
  • Provides assert-dot notation
  • Allows you to provide an additional message as last parameter, which will include as error message.

Below example will demonstrate the use of ‘assert


var assert = require('chai').assert;
var avengers = ['Iron man','Captain America','Hulk','Black Widow','Thor'];

describe('Chai Assert', function(){

    it('should return the length of avengers as 5 ', function(){
            assert.lengthOf(avengers,5,'Length of avengers is 5');
    });
    it('should return the type as string of 1st value value ', function(){
        assert.typeOf(avengers[0],'int','The data type should be string');
    })
    it('should return the 4th value from avengers ', function(){
        assert.equal(avengers[3],'Hulk','The 4th value of avangers is Black Widow');
    })
});

The output of above spec is below

Console Output of Chai Assert

*In case some test is failed then the output will be like below

Failed Steps on Console

BDD : BDD is the second type of assertion style in chai. BDD style comes in two flavors.

  • Expect : Expect is one of interfaces by which BDD style is exposed in chai. User can chain together natural language assertions.

Below example will demonstrate the use of ‘expect’


var expect = require('chai').expect;
var avengers = ['Iron man','Captain America','Hulk','Black Widow','Thor'];


describe('Chai Expect', function(){

    it('should return the length of avengers as 5 ', function(){
            expect(avengers).to.have.lengthOf(5);
    });
    it('should return the type as string of 3rd value value ', function(){
        expect(avengers[2]).to.be.a('string');
    })
    it('should return the 4th value from avengers ', function(){
       expect(avengers[4]).to.equal('Thor');
    })
});

The output of above spec is below

Output of Chai ‘expect’ on console

*expect also allows to add arbitrary messages which will be displayed as error message if the test is failed. Refer the example below.


var expect = require('chai').expect;
var avengers = ['Iron man','Captain America','Hulk','Black Widow','Thor'];


describe('Chai Expect', function(){

    it('should return the length of avengers as 5 ', function(){
            expect(avengers,'The length should be 5').to.have.lengthOf(4);
    });
    it('should return the type as string of 3rd value value ', function(){
        expect(avengers[2]).to.be.a('string');
    })
    it('should return the 4th value from avengers ', function(){
       expect(avengers[4],'The 4th value should be Thor').to.equal('Captain America');
    })
});

The output of above spec is below

Arbitrary messages displayed as error message on Console
  • Should  :  ‘Should’ style allows the same assertion as ‘expect’ interface, however it extends each object with a ‘should’property to the the assertion chain.

 Below example will demonstrate the use of ‘should’.


var should = require('chai').should();
var avengers = ['Iron man','Captain America','Hulk','Black Widow','Thor'];


describe('Chai Should', function(){

    it('should return the length of avengers as 5 ', function(){
            avengers.should.have.lengthOf(5);
    });
    it('should return the type as string of 3rd value value ', function(){
            avengers[1].should.be.a('string');
    })
    it('should return the 4th value from avengers ', function(){
            avengers[2].should.equal('Hulk');
    })
});
Output of Chai ‘should()’ on console

Differences :

The basic differences between ‘expect’ and ‘should’ are mentioned below.

  • expect’ requires is just a refernce to the‘expect’ function, whereas with the ‘should’ require, the function is being executed.

var expect = require(‘chai’).expect;

var should = require(‘chai’).should();

  • The ‘expect’ interface provides a function as a starting point for chaining your language assertions. It works on node.js and in all browsers. Whereas ‘should’  interface extends ‘Object.prototype’  to provide a single getter as the starting point for your language assertions. It works on node.js and in all modern browsers except Internet Explorer.

Testing Promises with Chai :

After testing a asynchronous function with callback here in this section we will talk about testing a promise with chai.

  • Promise  is asynchronus is nature. In a simple note, promise is like, let say we are trying to get some value through some code once it is executed but instead of the actual value we are getting something else (i.e. Promies), which says we will get the actual value once it is ready.
  • To test promise we will install one more chai library called ‘chai-as-promised
  • Installation : To install chai-as-promised run the below command.

npm install – -save-dev chai-as-promised’ // it will install as dev dependency Let’s understand through an example:


let listOfEmployee = ['Robart','Shyam','Abhishek','April'];
function isEmployeePresent(employeeName){
    return new Promise((resolve)=>{
        setTimeout(function(){
            resolve(listOfEmployee.indexOf(employeeName)>=0)
        },1000)
    })
}


module.exports = isEmployeePresent;

Output of the following specs is below

Output of Promise testing using Chai on Console

Mocha Reporter :

So in the last section of this article we are going to discuss about the mocha reporters. Mocha is coming with some default reporters which are discussed below.

  1. SPEC :  Alias: SPEC, spec (it is the default reporter)

2. DOT MARIX : Alias: DOT, dot (it displays failure status with ‘!’ and pending status with ‘,’ on the console)

3. NYAN : Alias: Nyan, nyan

4. TAP : Alias TAP, tap

5. LANDING STRIP :  Alias: Landing, landing

6. LIST : Alias: List, list (it displays a simple specifications list as test cases pass or fail)

7. PROGRESS : Alias: Progress, progress. (It implements a simple progress-bar)


8. JSON : Alias: JSON, json (It displays a single large JSON object)

9. JSON STREAM : Alias: JSONStream, json-stream

10. MIN : Alias: Min, min (it displays only the summary on the console)

*Note : Apart from these reporters mocha also supports some third party html reporters as well, like mochawesome, mocha-junit-reporter etc.

Summary 

In this article we have discussed about the concept of TDD and BDD along with the examples and we got to know about the mocha unit testing framework along with chai.

Ensuring Software Quality is an essential and difficult job, specially in certain situations where requirements are changing rapidly. In such conditions a good Test Framework can help to manage and execute test scripts frequently and efficiently. Mocha is one of them.

I wish this article can be helpful to it’s readers specially for them who wants to start exploring with mocha and chai to speed up their testing activities.

Thank You for your patience!

Leave a Reply

Up ↑

%d bloggers like this: