Testing with Mocha - Part 2

We installed Mocha and Chai and wrote a simple test case in Part 1. Now we continue to explore at a basic level what these testing tools have for us and will write some more test cases.

Testing objects

Assume we have the following object named books in app.js, because reading opens up new worlds:

const books = {
  title: 'The Lord of the Rings',
  genre: 'fantasy',
  author: 'J.R.R. Tolkien',
  pages: 1178
};

Let’s write some test cases on this object.

We need to export it first from app.js:

module.exports = {
  names,
  books
};

and then import it into app.test.js:

const { names, books } = require('./app');

The books object is an individual entity on its own, it’s not related to the names array, so we can create a new describe block for it:

describe('books object', () => {

});

We will write the assertions inside this block.

Objects can be tested in various ways. Chai offers quite a few tools for testing objects, so let’s have a look at some of them.

What about write a test case if books object has a certain property? This need often arises, especially when we need to build a feature on a particular property. We need to ensure that our code won’t break if the given property doesn’t exist or it has a falsy value.

Let’s see if the books object has the author property:

it('should have an author property', () => {
  expect(books).to.have.property('author');
});

Chai has a property method, which can be used for testing if an object has the given property. We need to pass a string as the parameter of the method. If we run this test, no surprise, it will pass, as our books object indeed has a property called author. Isnt’t the syntax nice and clear?

It can also be useful if we compare objects. Image that we receive an object as the response to an http request and want to make sure that the result is exactly what we expected.

Let’s write the next test case inside the same describe block in app.test.js:

it('should match objects', () => {
  const expectedBooksObj = {
    title: 'The Lord of the Rings',
    genre: 'fantasy',
    author: 'J.R.R. Tolkien',
    pages: 1178
  };

  expect(books).to.deep.equal(expectedBooksObj);
});

We have an expectation about the object we receive. The expectedBooksObj is exactly what its name suggests: we expect to get an object like this with the same keys and values. This is the object we want books to be like. The assertion clearly expresses this expectation.

We used the deep.equal Chai chain here, but we could have used the following assertion as well:

expect(books).to.eql(expectedBooksObj);

These two assertions are equivalent.

Testing functions

Finally, let’s have a look at a very simple function and how we can write some test cases.

The function we will test is the rarely if ever seen addNumbers function:

const addNumbers = (a, b) => {
  return a + b;
};

It’s not very hard, we feed in two numbers and will get their sum returned.

Let’s export the function first by adding it to the export object in app.js:

module.exports = {
  names,
  books,
  addNumbers
};

We again need to require it in app.test.js:

const { names, books, addNumbers } = require('./app');

Create a new describe block and challenge your computer whether it can correctly add 3 and 5:

describe('addNumbers', () => {
  it('should return the correct result', () => {
    const result = addNumbers(3, 5);
    expect(result).to.equal(8);
  });
});

If your computer is at least as smart as mine is, this test will pass. If you have a closer look at the test case, you’ll see that we have used the equal method here as opposed to eql in the last block when we tested the books object. While equal is supposed to test strict equality, the eql method is used for deep equality. If we go back to the books object test case and replace eql with equal, the test will fail and we will get a gentle warning that something is not good with our assertions.

Let’s play around with this function and change the second parameter to a string:

it('should return the correct answer when one of the parameters is a string type', () => {
  const result = addNumbers(3, '3');
  expect(result).to.equal(6);
});

If we run the test with npm test, it will fail and the error message will say that 33 is expected to equal 6. This is a clear indication that we need to change our addNumbers function.

Can we receive numbers of string type instead of numbers of number type? I think it’s possible.

This is now a very basic example of the method called test driven development or TDD. We write a test case first, and when it fails, we change the function so that the test passes. Let’s do this then in app.js and modify the addNumbers function accordingly:

const addNumbers = (a, b) => {
  return Number(a) + Number(b);
};

If we run the test again, it will pass.

Now that we play around with strings, let’s enter a “real” string. We modified the addNumbers in the last step, we should get NaN:

it('should return NaN when one of the parameters is not a number', () => {
  const result = addNumbers(3, 'hi');
  expect(result).to.eql(NaN);
});

Indeed, this test case will also pass. Just for fun, what about using strict equal? Replace the eql in the assertion with this one:

expect(result).to.equal(NaN); // fails

The failure of this test is no surprise as NaN is not equal to itself.

Conclusion

Part 2 concludes the introduction to Node.js testing tools Mocha and Chai. The Chai assertion library is easy to use and learn and it clearly indicates what our test case is about.

Although I didn’t like writing tests in the beginning, now I actually do them, because I feel more confident about my code. Writing unit test is not a question, we always want to cover our code (and our back).

I hope that you find this writing useful and if you haven’t written tests so far, just start making friends with them through easy examples like these.