Test Driven Development with Node.js


[written by Roman Kollatschny and Matthias Schmidt]

This is a series of blog posts which addresses different development topics like test driven development, code style and code quality for the Node.js runtime.
The series requires some basic knowledge about JavaScript, Node.js and basic webdevelopment techniques in general.
It will start with this first article about test driven development on Node.js using the tools Mocha and Chai.

Most of us have heard something of test driven development, but only a very few know the methods of this development style and even less do really develop test driven. Since we only have heard the principles of testing in software development but never really were able to use them in practice, we decided to dive into this topic. To give you also a benefit of our diving experience, we want to show you, how to develop test driven.

Test driven development or TDD is a process in software development. The process evolved from the test-first programming concept of extreme programming. In comparison to classic development the test cases are defined before the application implementation starts.

The TDD tests are written as grey-box tests. That means, that they contain parts form white and from black-box tests. Black-box tests are written without the knowlege of the inner functions of the system that is tested. It is limited on function-oriented testing, so tests only base on the requirements. The system is treated as a black box where only information which is accessible from the outside influences the test. However white-box tests are definded with the knowlege of how the systems is implemented.

The grey-box and white-box tests share in common, that they are implemented by the same developers, that implement the system later.

Black-box tests are definded whitout awareness of the internal structure of the application to be tested, while a white-box test needs knowlege of the internal structure or even developed by the application developer itself. A grey-box test is written with some knowledge of the internal structure, because the test developer also implements the application following the tests.

If you want to develop test driven you have to follow some process according to the TDD cycle.

TDD cycle

TDD Cycle
TDD Cycle

The test driven development cycle consists of 5 easy steps:

  1. Add a test
    Write a test case to cover the requirements of our application. For the first iteration your test should be as small as possible.
  2. Run the test
    After defining our test we’re going to execute the test against the application. The test should not pass because the requirement isn’t implemented yet.
  3. Write the code
    To fix this it’s time to implement the least necessary code in our application. Don’t do too much! Only the really necessary parts should be implemented.
  4. Run the test again
    If we run the previous defined test again the test should pass. Otherwise we have to go over the code again until it passes.
  5. Refactor the code
    When the test passes we should do some code refactoring to keep our code clean and slim.

Repeat
For each requirement of our application the previous steps need to be repeated until all functionalities are implemented and all tests pass.

But TDD as a whole is more than only this process. A big and meaningful part is the developers mindset. At the beginning it may sound weird to develop the TDD way, but later it will become more clear why it’s done that way. If your’re interested in learning TDD give it a try and stop thinking about the differences for the moment – we know, this is one of the hardest parts.

TDD in Node.js

There are multiple ways to work with TDD in Node.js. You can use the integrated assertion library or choose a framwork like Jasmine. In this article series we want to focus on the test runner Mocha and the assertion library Chai.

Mocha

Mocha is a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple and fun. – Mochajs.org

Mocha can be used with different assertion libraries like should.js, expect.js, better-assert, unexpected or Chai.

Mocha executes our defined tests on the code and reports the results to us. An quick start guide and the documentation can be found on mochajs.org.

Chai

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

Chai.js is a assertion library with different included styles the developer can choose from. This includes assert, should and expect style. Chai can be extended by using some plugins, e.g. for web requests or file system access.

In the following we are using the behaviour driven development (BDD) style ‘expect’ where tests are written from a business perspective. A complete overview of the chai api can be found on chaijs.com.

Testing syntax

With our test runner Mocha we can specify a test suite and a test case. A test suite is a collection of multiple test cases. For example there is a test suite of a class in an application.

A test suite can be definded with the describe method of Mocha. The first argument for this method is a string which describes the test suite, e.g. ‘database tests’. The second argument is an simple callback function, which includes the definded test cases.
It is also possible to nest these blocks to get a more detailed structure.
Example:

describe('database tests', function() {
  describe('users table tests', function () {
    // Test cases
  }
}

A test case is one single requirement of the test suite. The test case includes the actual test functions.
Test cases are defined with the it method from Mocha. This method uses also a string argument for its description. The second argument is again an function block which includes the test functions.

describe('database tests', function() {
  describe('users table tests', function () {
    it('returns an array', function(){
      // test functions
    });
  }
}

To define the tests, we need an assertion library. As stated, we are going to use Chai. Chai provides three different assertion styles: should, expect and assert. The chainable behaviour driven development style of should and expect provide an expressive language and a more human-readable style. Otherwise the assert style has a more classic approach.

Before defining the test functions, it’s necessary to import the module via require and choose your preferred assertion style.

let expect = require('chai').expect;

The expect syntax uses the method expect. It needs an argument on which will be tested. To test the given argument, the test functions can be chained with the following parts: to, be, been, is, that, which, and, has, have, with, at, of, same. All possible test methods are listed in the chai.js API Reference.

expect(users).to.be.an('array')
  .and.is.not.empty();

In this example we run our tests synchronously. To run them asynchronously we have to add a callback function (e.g. done(); like in Mocha examples).

In the next blog article we are going to adapt the TDD cylce in an little helloWorld example.