Mocha & Chai: Your first test

Mocha

Mocha is a test runner. It's sole job is to look for (or be told where to find) unit tests you've written, and to execute them. It also displays in a nice, easy format to show you how your unit tests faired - how many passed, how many and which ones failed.

Mocha Website:
https://mochajs.org/

Chai

Chai is an assertion library. Chai's libraries allow you to choose exactly how your unit tests should read to a developer. There are three libraries you get to choose from: assert, expect, and should.

All of these libraries essentially do the same thing, just with slightly different syntaxes. For simplicity, we're going to use the assert library, but you should definitely check out how tests are written in the expect and should libraries. (It's pretty nice, because they read like english).

Chai Website:
http://chaijs.com/

Assert API:
http://www.chaijs.com/api/assert/

What do we need?

First we will install Mocha & Chai.

npm install -g mocha
npm install --save chai

We are installing Chai locally to our nearest folder, but we are installing Mocha globally. Installing Mocha globally saves us a lot of hassle which is why we are doing it that way instead of locally.

After these have been installed, we will create two files:

  • app.js
  • test.js

In our app.js file we will put the material that we want to run the test on. In our example we will just be writing a simple function algorithm.

In our test.js file is where we will write the test that we are going to run. This is also where we will be importing Chai.

var words = ['dog', 'cat', 'camel', 'rat'];

function addS (arr) {
    return arr;
}

module.exports = addS;

app.js

var chai = require('chai');
var assert = chai.assert;
var addS = require('./app');

describe("", function(){
  it("", function(){
    assert.equal(<actual>, <expected>)
    })
  })

test.js

Ok, now we can break down our files to see just exactly what is happening.

In our app.js file we are just writing an empty function. That function will eventually do what we want it to. But for now we are leaving it empty.

The last line on the file is module.exports = addS;

This line is allowing us to export, or send, the content of that file to be used in another file, which in our case will be the test.js file.

Let's take a look at our test.js file.

First, we are importing Chai into our document.

As stated above, we only want to use the assert library for now, so the next line is just assigning the assert library to a variable assert.

Next, we are importing the contents of our app.js file into our test.js file so that we can use our function in our test.

Finally, we see a few functions that seemingly come from nowhere.

describe and it

These both come from the globally installed Mocha.

The describe is used to title the algorithm or function or to describe what it is supposed to do.

The it is used to tell what each test you write should or should not do.

The assert.deepEqual is from our Chai Assert Library.

The .deepEqual asserts that actual is deeply equal to expected.
(.deepEqual is for comparing arrays or objects, if we were comparing simple data types like numbers, booleans, strings, etc.. we would simply use .isEqual instead)

The assert.deepEqual also takes 2 arguments.

  • The actual - This is where we are running our function addS(). It will return whatever the function is set to return.

  • Ours is currently set to simply return the same array that it receives without any alterations to the original array.

  • The expected - This is where we put what we want the outcome of our function to look like.

  • Since we said 'It should end with an s', then our expected will be whatever array we pass in with and 's' added to the end.

Let's fill out our test.js and look at an example of it running.

var chai = require('chai');
var assert = chai.assert;
var addS = require('./app');

describe("Create a function that adds an *s* to the end of every word in an array", function(){
  it("should end with an s", function(){
    assert.deepEqual(addS(['dog', 'cat', 'alligator']), ['dogs','cats','alligators'])
    })
  })

Go into your terminal and type mocha.

It should return something that looks like this:

Notice that the first line in white is the same as what we wrote in our describe. The second line in red is the same as what we wrote in our it.

It then runs the test to check if:

Our Actual

  • addS(['dog', 'cat', 'alligator']) which returns ['dog', 'cat', 'alligator']

is equal to

Our Expected

  • ['dogs', 'cats', 'alligators']

Since ['dog', 'cat', 'alligator'] is not equal to ['dogs', 'cats', 'alligators'] it gives us an error showing how the test failed.

So now we will go to our app.js and fix it so that our test will pass.

var words = ['dog', 'cat', 'camel', 'rat'];

function addS (arr) {
  for(var i = 0; i < arr.length; i++){
    arr[i] += 's';
  }
    return arr;
}

module.exports = addS;

Now if we run mocha it will look like this.

Awesome!

Now let's add a couple additional tests.

var chai = require('chai');
var assert = chai.assert;
var addS = require('./app');

describe("Create a function that adds an *s* to the end of every word in an array", function(){
  it("should end with an s", function(){
    assert.deepEqual(addS(['dog', 'cat', 'alligator']), ['dogs','cats','alligators'])
    })
  it("should not allow numbers into the array", function(){
    assert.equal(addS(['dog', 0, 'alligator']), 'no numbers')
    })
  it("should not add an 's' if the word ends with an 's'", function(){
    assert.deepEqual(addS(['dog', 'moss', 'log']), ['dogs', 'moss', 'logs'])
    })
  })

If we run mocha it will look like this:

So we would now need to fix our algorithm so that it passes all of the tests.

var words = ['dog', 'cat', 'camel', 'rat'];

function addS (arr) {
  for (var i = 0; i < arr.length; i++){
    if(typeof arr[i] === 'number'){
      return 'no numbers'
    }else if (arr[i].lastIndexOf('s') !== arr[i].length-1) {
      arr[i] += 's'
    }
  }
  return arr;
    
}


module.exports = addS;

Now mocha will look like this

After seeing all this, perhaps it has crossed your mind to ask why this is necessary??

Below is a recap:

What is unit testing?

Unit testing is the practice of breaking your program into smaller pieces and inspect each piece with tests.

Tests run when changes are made to the code in your projects. The more often you run your tests the better.This helps you catch problems sooner and write better code.

Why should we be unit testing our code?

  • When new developers hear of unit testing their first thoughts are why? Doesn't this add more work and more code? In reality, by making a test for each piece of application we improve a number of things.

  • Makes your code easier to understand by forcing you to write out the expected output of your program.

  • Makes it easy to add new features. Normally when we add new code we have to go through the application and make sure it still works as it did before. Now we can simply run are tests and have proof that it works as it did before.

  • Protects your code against other developers. By writing out the way your program should behave if other developers change your code they will see immediately see when they break the application.