How to make tests using chai and mocha

This tutorial is intended to show you the theory of TDD and how you can use this in your node application using chai and mocha.

In this article, I’ll show you how we can make tests using two awesome tools: chai and mocha.

But first of all, I’d like to tell about the importance of testing. Why tests are so necessary? For this, I should explain what TDD is.

The Test-Driven Development (TDD) is a software engineering process that requires unit tests to be written before the code they are supposed to validate that relies on the repetition of a very short development cycle, where the requirements are transformed in test cases. With this, firstly the code will fail (miserably), then the developer should write clean code that works to make the tests pass.

The cycle is very simple. The developer writes a test, this test will fail because you haven’t developed any code for these tests, so the developer should check if the tests are failing. After this, the developer writes clean code to make the tests pass. Again, the developer should check if all tests are passing, if not, it should go back to write code. And, finally, we should remember to make our code always better and refactor it to create new tests.

This is image title
Having this in mind, we can talk about chai and mocha.

Mocha logo

Mocha is a JavaScript test framework running on Node.js and in the browser. Mocha allows asynchronous testing, test coverage reports, and use of any assertion library.

This is image title

Chai is a BDD / TDD assertion library for NodeJS and the browser that can be delightfully paired with any javascript testing framework.

Basically, mocha is a framework and chai is a library. Let’s go a little deeper in mocha.

Mocha: how can I test?

Mocha uses hooks to organize its structure. Let’s talk about them.

  • describe(): It’s used to group, which you can nest as deep;
  • it(): It’s the test case;
  • before(): It’s a hook to run before the first it() or describe();
  • beforeEach(): It’s a hook to run before each it() or describe();
  • after(): It’s a hook to run after it() or describe();
  • afterEach(): It’s a hook to run after each it() or describe();

Let’s see some scenarios

Scenario 1: One test case

In this case, we can just call a solitary it(), mocha will run this only test.

it('Homer should drink beer', () => {
  /** Test cases */
})

mocha-one-test-case.js

Scenario 2: A nested test case

In this case, we can nest some describes() hooks and finally call it() to execute the test.

describe('Abraham Simpson', () => {
  describe('Homer Simpson', () => {
    describe('Bart Simpson', () => {
      it('Bart should skate', () => { 
        /** Test cases */ 
      })
    })
  })
})

mocha-one-nested-test-case.js

Scenario 3: Two test cases in one test

Here, inside the describe() we have two it() that will execute the tests.

describe('Homer Simpson', () => {
  it('Bart should skate', () => { 
    /** Test cases for Bart */ 
  })
  it('Lisa should play sax', () => { 
    /** Test cases for Lisa */ 
  })
})

mocha-two-tests-in-one-test.js

Scenario 4: Run just once before the first test case

In this scenario, before the first it() mocha will execute what is inside before(), but only once.

describe('Springfield', () => {
	before(() => {
		console.log('Marge calls Barge and Lisa (you see this only once)')
	})
	it('Bart should hear his mother', () => {
		/** Test cases */
	})
	it('Lisa should hear her mother', () => {
		/** Test cases */
	})
})

mocha-run-just-once-before-first-test-case.js

Scenario 5: Run once before each test case

On the contrary of before(), beforeEach() is executed each time for each it() [or describe()] that we have. If we have one it(), it will be executed just once. if we have two it() it will be executed twice. If we have three it() it will be executed three times and so go on.

describe('Springfield', () => {
	beforeEach(() => {
		console.log('Marge calls Barge and Lisa (you see this twice)')
	})
	it('Bart should hear his mother', () => {
		/** Test cases */
	})
	it('Lisa should hear her mother', () => {
		/** Test cases */
	})
})

mocha-run-once-before-each-test-case.js

Scenario 6: Two tests in a big test

In this last scenario, mocha will nest the describe() and execute it().

describe('Homer Simpson', () => {
	describe('Bart Simpson', () => {
		it('Bart should skate', () => {
			/** Test cases */
		})
	})
	describe('Lisa Simpson', () => {
		it('Lisa should play sax', () => {
			/** Test cases */
		})
	})
})

mocha-two-test-big-test.js

Hands-on!!!

To see it working, let’s do firstly a simple case: A calculator. Let’s create a test case where our calculator should calculate the following operations:

  • Addition
  • Subtraction
  • Multiplication
  • Division

We’ll use all the TDD structure using mocha.

Creating the project

First, let’s create a project called mocha-chai-tests.

$ mkdir mocha-chai-tests
$ cd mocha-chai-tests
$ yarn init
or 
$ npm init -y

Install chai and mocha.

$ yarn add chai mocha
or 
$ npm i chai mocha --save

Inside the project mocha-chai-tests, create a folder called tests and a file called calc.js.

$ mkdir tests
$ touch tests/calc.js

Now, we can create our first test. To do this, we’ll create a describe() indicating that we’re doing tests on a calculator and another one indicating the addition operation. Let’call a function called add that will sum two numbers. Let’s test if 1 plus 1 will be 2.

const chai = require('chai')
const expect = chai.expect

const calculator = require('../src/calculator')

describe('Calculator', () => {
	describe('Addition', () => {
		it('1 + 1 should be equals to 2', () => {
			expect(calculator.add(1, 1)).to.equal(2)
		})
	})
})

test-calc.js

Hey Sam, but the file src/calculator.js doesn’t exist. How can we handle this?

It doesn’t exist, but it should. So much that, if we run the tests we’ll get an error. Let’s do this to see what happens. Type the following command on terminal:

$ yarn mocha tests/calc.js

This is image title

So, to fix this let’s create the folder src and an empty file called src/calculator.js.

$ mkdir src
$ touch src/calculator.js

If you run the tests again you’ll see another result.

$ yarn mocha tests/calc.js

This is image title

Realize that you can see the structure contained by the Calculator, the Addition, and the failing of our test. The tests tell us the problem is calculator.add is not a function. This is because we do not have any code in the src/calculator.

Before to start developing the functions on src/calculator.js, let’s improve our tests.

const chai = require('chai')
const expect = chai.expect

const calculator = require('../src/calculator')

describe('Calculator', () => {
	describe('Addition', () => {
		it('1 + 1 should be equals to 2', () => {
			expect(calculator.add(1, 1)).to.equal(2)
		})
		it('should sum two numbers', () => {
			expect(calculator.add(2, 2)).to.equal(4)
			expect(calculator.add(50, 39)).to.equal(89)
			expect(calculator.add(-31, 32)).to.equal(1)
			expect(calculator.add(10000, 89999)).to.equal(99999)
		})
	})

	describe('Subtraction', () => {
		it('1 - 1 should be equals to 0', () => {
			expect(calculator.subtract(1, 1)).to.equal(0)
		})
		it('should subtract two numbers', () => {
			expect(calculator.subtract(6, 2)).to.equal(4)
			expect(calculator.subtract(50, 39)).to.equal(11)
			expect(calculator.subtract(-31, 32)).to.equal(-63)
			expect(calculator.subtract(10000, 89999)).to.equal(-79999)
		})
	})

	describe('Multiplication', () => {
		it('1 * 1 should be equals to 1', () => {
			expect(calculator.multiply(1, 1)).to.equal(1)
		})
		it('should multiply two numbers', () => {
			expect(calculator.multiply(3, 2)).to.equal(6)
			expect(calculator.multiply(50, 39)).to.equal(1950)
			expect(calculator.multiply(-31, 32)).to.equal(-992)
			expect(calculator.multiply(-5, -2)).to.equal(10)
		})
	})

	describe('Division', () => {
		it('1 / 1 should be equals to 1', () => {
			expect(calculator.divide(1, 1)).to.equal(1)
		})
		it('should divide two numbers', () => {
			expect(calculator.divide(4, 2)).to.equal(2)
			expect(calculator.divide(50, 5)).to.equal(10)
			expect(calculator.divide(-15, 2)).to.equal(-7.5)
		})
		it('should return NaN if the denominator is zero', () => {
			expect(calculator.divide(4, 0)).to.equal(undefined)
			expect(calculator.divide(50, 0)).to.equal(undefined)
			expect(calculator.divide(-15, 0)).to.equal(undefined)
		})
	})
})

tests-calc.js

Explaining what I did. I create the main structure called Calculator. I’m testing the four math operations: addition, subtraction, multiplication, and division, where I structured them in each describe(). So, in addition, I’m testing if 1+1=2, and them I’m using the random numbers, where I know the result of the sum of them, and making sure if my function is returning the correct value. I’m following the same idea on the subtraction, multiplication, and division. However, in the division, I’m adding a new test: if the denominator (divisor) is zero, we should return undefined, because this operation is not allowed.

For curiosity, you can submit it to tests again and see what happens.
This is image title

Now we can make the necessary changes to make our tests pass. Let’s edit the file src/calculator.js.

const add = (a, b) => a + b
const subtract = (a, b) => a - b
const multiply = (a, b) => a * b
const divide = (a, b) => b !== 0 ? (a / b) : undefined

module.exports = {
    add,
    subtract,
    multiply,
    divide,
}

src-calculator.js
If we submit the tests again…

This is image title

Now all the tests are passing.

Using chai and mocha to test API

As we know to make tests using chai and mocha, let’s test this using APIs. Let’s create our folder and files. In the same folder mocha-chai-tests, create:

$ touch tests/api.js

And now, let’s install a new dependency: chai-http. It will help us to make requests from a server. And for this, let’s take and advantage and install express.

$ yarn add chai-http express 
or
$ npm i chai-http express

For this case, I’ll create a function called createFakeServer, that means it will create a server during the tests and, in the final, will close it. Besides, I’ll use different test cases on chai. I’ll just create an endpoint that will return a particular object with some information and we will validate it.

const chai = require('chai')
const chaiHttp = require('chai-http')
const express = require('express')

const expect = chai.expect

chai.use(chaiHttp)

const createFakeServer = () => {
    const app = express()
    const apiPort = 30001
    const result = {
        message: 'Hello World',
        value: 3.5,
        odds: [1,3,5,7,9],
        person: {
            id: 123, 
            name: 'Sam Barros',
        }
    }

    app.get('/', (req, res) => {
        res.send(result)
    })

    app.listen(apiPort)

    return app
}

describe('API', () => {
	describe('GET /', () => {
      let fakeServer

      beforeEach(() => {
          fakeServer = createFakeServer()
      }) 

	  it('should return the object', (done) => {
          chai.request(fakeServer)
              .get("/")
              .end((err, res) => {
                  expect(err).to.be.null
                  expect(res).to.have.status(200)

                  expect(res.body).to.be.a('object')

                  expect(res.body).to.have.property('message')
                    
                  expect(res.body).to.have.property('value')
                  expect(res.body.value).to.be.a('number')

                  expect(res.body).to.have.property('odds')
                  expect(res.body.odds).to.be.a('array').with.lengthOf(5)
                  expect(res.body.odds).to.be.an('array').that.does.not.include(2)
                  expect(res.body.odds).to.deep.include(5)
                  expect(res.body.odds).to.have.ordered.members([1, 3, 5, 7, 9]).but.not.have.ordered.members([3, 1])

                  expect(res.body).to.have.property('person')
                  expect(res.body.person).to.have.property('id')
                  expect(res.body.person).to.have.property('name')
                  expect(res.body.person.name).to.be.string
                  expect(res.body.person.name).to.deep.equal('Sam Barros')

                  done()
              })
          })
	})
})

mocha-api.js

To close the server at the end of the submission, we should use the “–exit” mocha option.

$ yarn mocha --exit tests/api.js 

We should pay attention to something. Because the end function is passed a callback, assertions are run asynchronously. That means a mechanism must be used to notify the testing framework that the callback has completed. Otherwise, the test will pass before the assertions are checked. In the Mocha, this is accomplished using the done callback, which signals that the callback has completed, and the assertions can be verified. 🙂

Conclusion

This tutorial is intended to show you the theory of TDD and how you can use this in your node application using chai and mocha.

I’d like to say thanks to you, my friends, for dedicating time reading this article.

#nodejs #chai #mocha

How to make tests using chai and mocha
1 Likes22.95 GEEK