Containerized Testing for Node Applications with Dockunit

Free Course

Build Your First Node.js Website

Node is a powerful tool to get JavaScript on the server. Use Node to build a great website.

This tutorial covers setting up containerized unit testing for Node applications. Containerized unit testing allows you to ensure your software works across a wide variety of platforms.

Overview

Writing and distributing Node.js applications necessitates supporting as many versions of Node.js as possible. npm, the most popular Node.js package manager, requires Node.js 0.8 or higher. Therefore to support your users, potential downloaders, or even your customers, it behooves you to test your program on Node.js versions 0.8 and up. Node.js 0.8 was released in 2012; many versions have been released since then. io.js and Node.js were combined in version 4.0.0. You have a lot of testing ground to cover!

There are a few ways to run your code in multiple versions of Node.js: n and nvm are npm packages that allow you to switch between different versions of Node.js. It’s difficult quickly switch between versions to test your code. It’s also difficult to batch run your code against a list of versions. Travis CI is a continuous integration service that allows you to run your code across multiple versions of Node.js. However, you are stuck with the versions of Node.js that they want to support, or you can use their new Docker based system which is somewhat experimental and cumbersome to operate.

Neither of these ways did the trick for me. Enter Dockunit.

Dockunit is a utility for running containerized software tests. Dockunit.io is a continuous integration service built for Dockunit. Let’s setup Dockunit to be used with our Node.js application.

Prerequisites:

  • A Unix based operating system such as OSX or Ubuntu.
  • Docker (boot2docker in case of OSX) A version of Node.js and npm
  • Basic terminal knowledge Basic knowledge of JavaScript and Node

Part 1: Develop a Simple Node Application (skip if you have an existing application)

We need a simple Node application to test for the purposes of this tutorial. We will write a simple app that prints out the current system time that includes one basic unit test. The code for this tutorial is available on Github.

Let’s start by creating a folder on your machine:


/test-app

Next, let’s initialize a package.json file from within the /test-app directory. In the terminal enter:


npm init

Just press enter on all the prompts. No need to specify all this information for our simple application. Next let’s create a file time.js such that our application looks like this:


/test-app
    time.js

In time.js paste the following code:


'use strict';

class Time {
    constructor() {
        this.date = new Date();

        this.hour = this.date.getHours();
        this.hour = (this.hour < 10 ? '0' : '') + this.hour;

        this.min  = this.date.getMinutes();
        this.min = (this.min < 10 ? '0' : '') + this.min;

        this.ampm = 'AM';
        if (this.hour > 12) {
            this.ampm = 'PM';
            this.hour -= 12;
        }
    }

    getPretty() {
        return 'The time is ' + this.hour + ':' + this.min + ' ' + this.ampm;  
    }
}

module.exports = Time;

This code creates a basic ES6 class to wrap the transformed JavaScript Date object into usable time information. The wrapper also let’s you print out the current time in an easy to read way.

Now let’s create the main file to utilize our time class. Create a file index.js like so:


/test-app
    index.js

In index.js paste the following code:


'use strict';

var colors = require('colors');
var Time = require('./time');

var time = new Time();

console.log(time.getPretty().green);

This code just instantiates a Time object and prints the current time to the screen. colors is a npm package that let’s you give your terminal output some color. We need to install the npm package for this to work. In the terminal run the following command from within the /test-app directory:


npm install colors --save

Now let’s write a simple unit test with Mocha. First, install Mocha globally:


npm install -g mocha

Now create a folder test and a file time.js inside of it like so:


/test-app
    index.js
    time.js
    /test
        time.js

In time.js paste the following code:


'use strict';

var assert = require('assert');
var Time = require('../time');

describe('time', function() {

    describe('#constructor', function() {

        it('Object initializes hour, minute, and AM/PM', function() {
            var time = new Time();

            assert(time.hour);
            assert(time.min);
            assert(time.ampm);
        });
    });
});

This code tests up a simple unit test that instantiates a Time object and makes sure the proper instance variables are setup.

Run the following command in your terminal from the root of your project:


mocha

If you have setup your tests correctly, it should show you one test is passing.

That’s it! You now should have a simple node app with a basic unit test setup. Again, you can find all the code for this tutorial on Github.

Part 2: Setup Dockunit

We want our application to work across many different versions of Node since we will be distributing it publically. We need to be able to quickly run our tests locally across a number of Node versions. We also want our teammates and collaborators to be able to run the same tests without having to setup complex local environments. We will use Dockunit to accomplish this.

First, install Dockunit locally. In the terminal run the following command:


npm install -g dockunit

In the root of the test-app folder, create the file Dockunit.json like so:


/test-app
    index.js
    Dockunit.json
    time.js
    /test
        time.js

In Dockunit.json paste the following code:


{
    "containers": [
        {
            "prettyName": "Node 0.10.40",
            "image": "node:0.10.40",
            "beforeScripts": [
                "npm install",
                "npm install -g mocha"
            ],
            "testCommand": "mocha"
        },
        {
            "prettyName": "Node 0.12",
            "image": "node:0.12",
            "beforeScripts": [
                "npm install",
                "npm install -g mocha"
            ],
            "testCommand": "mocha"
        },
        {
            "prettyName": "Node Latest",
            "image": "node:latest",
            "beforeScripts": [
                "npm install",
                "npm install -g mocha"
            ],
            "testCommand": "mocha"
        }
    ]
}

Dockunit.json files are written in JSON which is JavaScript object notation. All Dockunit.json files contain a root level object with a property containers which holds an array of objects that each describe a test to be run in a Docker container. The idea is for containers to be different real life simulations of your software so you can see how it performs in different scenarios. Here is an example container object:


{
    "prettyName": My container pretty name",
    "image": "docker-image-ref",
    "beforeScripts": [
      "before-command-1",
      "before-command-2"
    ],
    "testCommand": "my-command"
}

prettyName is just an easy way for you to describe your container which will be useful in build output. image refers to a Docker Hub image. Dockunit comes with a bunch of predefined images that you can use, however you can always create your own. beforeScripts allows you to run commands in the Docker container before the test command is run. testCommand is the command whose exit code will be evaluated to determine whether your software “passes” or not.

This particular Dockunit.json file describes three containers, one for Node 0.10.40, Node 0.12, and the latest version of Node. Let’s run Dockunit. In the terminal run the following command from the root of your test-app folder:


dockunit

The first time you run the command it will take a few minutes to download all the relevant Docker images. Subsequent runs will be much faster. You should see your test output for all three containers. If you are using the example application, 1 out of the 3 containers will show as passed.

What happened?

Well, in test-app/time.js we are using ES6 class style syntax which is only available in newer versions of Node. Let’s refactor our code to make it more compatible with older versions of Node. Paste the following code into test-app/time.js:


'use strict';

function Time() {
    this.date = new Date();

    this.hour = this.date.getHours();
    this.hour = (this.hour < 10 ? '0' : '') + this.hour;

    this.min  = this.date.getMinutes();
    this.min = (this.min < 10 ? '0' : '') + this.min;

    this.ampm = 'AM';
    if (this.hour > 12) {
        this.ampm = 'PM';
        this.hour -= 12;
    }
}

Time.prototype.getPretty = function() {
    return 'The time is ' + this.hour + ':' + this.min + ' ' + this.ampm;  
}

module.exports = Time;

Rerun Dockunit by running the following command in your terminal from the project root:


dockunit

Now all your containers should be passing. See how easy it is test your code against multiple versions of Node? Since the images are distributable and live on Docker Hub, anyone can run Dockunit on your project without setting up a local environment.

Part 3: Setup Continuous Integration with Dockunit.io

Now that we’ve setup Dockunit for our project and pushed to Github, we can set up continuous integration on Dockunit.io so Dockunit tests are run on each pull request and push to the repository. If a pull request or push fails, a failed status will be shown on Github so we can fix our software.

First, create an account on Dockunit.io.

Next, go to projects and integrate your account with Github:

dockunit-authorized-github

Within Dockunit.io, create a project for your Github repository:

dockunit-new-project

That’s it! You can now see the build status of your project. It will update automatically when you push to Github:

dockunit-dash

Within Github, you can see the Dockunit build status within each Pull Request:

dockunit-test

Conclusion

In this tutorial we have created a simple Node application, setup Dockunit for local containerized unit testing, and integrated our project’s Github repository with Dockunit.io for continuous containerized integration testing. Containerized software testing is the future of assuring code integrity. Give it a try!

Taylor

Open source web technologies engineer and enthusiast. Lover of all things Node.js and WordPress. Creator of Dockunit and Dockunit.io. Director of Web Engineering at 10up.