Built.io Blog

Node.js Unit Testing with "Tape"

,

Unit testing pushes developers to test individual unit of code and find bugs. With unit testing in place, developers can refactor or add a new feature at any time without the fear of breaking their existing code. With proper unit testing in place and continuous testing, developers can build robust software components without slowing down.

Benefits of Unit Testing

Find Software Bugs Early

Unit testing helps developers find bugs early in the development cycle. This includes implementation issues and flaws or missing specifications for the unit.

Smoother Changes

Unit tests ensure that the code still functions properly as the code base changes with code refactoring and expansion.

Simplifies Integration

Unit testing verifies the accuracy of the each unit. By testing parts of the application before integrating them into an application, the testing process for the application is simplified and faster because the individual units have already been verified.

Design

When software is developed using a test-driven approach, the combination of writing the unit test and the refactoring activities performed after successful testing can replace formal design. This optimizes the design phase by identifying all the use cases for the functionality instead of designing the functionality and then designing the test cases for each functionality.

Tape

There are many libraries and modules available for unit testing in Node.js; today we will use Tape.

Tape is a simple TAP (Test Anything Protocol) producing library for Node and browsers. The TAP is a simple text-based interface between testing modules and a test harness. It is a simple way to list your test results.

Getting started

Installation

npm install tape --save-dev

First, we need to install Tape in our Node.js application under dev-dependencies (it will only be required in the development environment).

Start developing your test cases by requiring Tape in each file, then move them into a single file and finally, execute them using Tape.

Example

var test = require('tape');
test('Calculator Test Cases', function (TC) {
    TC.test('addition test', function(assert) {
        assert.equal(2+2, 4, '2 + 2 = 4');
        assert.end();
    });
    TC.test('subtraction test', function(assert) {
        assert.equal(2-2, 0, '2 - 2 = 0');
        assert.end();
    });
    TC.test('multiplication test', function(assert) {
        assert.equal(2*2, 4, '2 * 2 = 4');
        assert.end();
    });
    TC.test('division test', function(assert) {
        assert.equal(2/2, 1, '2 / 2 = 1');
        assert.end();
    });
    TC.test('module test', function(assert) {
        assert.equal(2%2, 0, '2 % 2 = 0');
        assert.end();
    });
});

Run the test using following

$ node index.js

Output

TAP version 13
# Calculator Test Cases
# addition test
ok 1 2 + 2 = 4
# subtraction test
ok 2 2 - 2 = 0
# multiplication test
ok 3 2 * 2 = 4
# division test
ok 4 2 / 2 = 1
# module test
ok 5 2 % 2 = 0
1..5
# tests 5
# pass  5
# ok

Reporting

Tape also provides the result of unit tests in a readable format in the console. For reporting, there are a number of libraries available in the npm.

To use theses libraries, try the following:

node test/index.js | tap-spec // type the module name from npm for reporting

You can also customize the reporting by creating your own reporter. Find more details here.

Object Stream Reporter

You can create your own custom test reporter using this createStream() API. By default, the stream will be a text stream of the TAP output, but you can get an object stream instead by setting opts.objectMode to true.

var test = require('tape');
var path = require('path');
test.createStream().pipe(process.stdout);
process.argv.slice(2).forEach(function (file) {
    require(path.resolve(file));
});

You can substitute process.stdout for any other output stream you want, like a network connection or a file.

Pass in the test files to run as arguments:

$ node tap.js
TAP version 13
# (anonymous)
not ok 1 should be equal
  ---
    operator: equal
    expected: "boop"
    actual:   "beep"
  ...
# (anonymous)
ok 2 should be equal
ok 3 (unnamed assert)
# wheee
ok 4 (unnamed assert)
1..4
# tests 4
# pass  3
# fail  1

Rendering An Object Stream

Here's how you can render an object stream instead of TAP:

var test = require('tape');
var path = require('path');
test.createStream({ objectMode: true }).on('data', function (row) {
    console.log(JSON.stringify(row))
});
process.argv.slice(2).forEach(function (file) {
    require(path.resolve(file));
});

The output for this runner is:

$ node object.js
{"type":"test","name":"(anonymous)","id":0}
{"id":0,"ok":false,"name":"should be equal","operator":"equal","actual":"beep","expected":"boop","error":{},"test":0,"type":"assert"}
{"type":"end","test":0}
{"type":"test","name":"(anonymous)","id":1}
{"id":0,"ok":true,"name":"should be equal","operator":"equal","actual":2,"expected":2,"test":1,"type":"assert"}
{"id":1,"ok":true,"name":"(unnamed assert)","operator":"ok","actual":true,"expected":true,"test":1,"type":"assert"}
{"type":"end","test":1}
{"type":"test","name":"wheee","id":2}
{"id":0,"ok":true,"name":"(unnamed assert)","operator":"ok","actual":true,"expected":true,"test":2,"type":"assert"}
{"type":"end","test":2}

I hope I’ve inspired you to use Tape to perform unit testing on your application. It’s simple to use and extremely useful when it comes to ensuring your app is error-free.

Subscribe to our blog