FacebookLinkedInShare

Motivation

Working with agile methodologies makes unit tests a crucial step in the development process. Combined with the amazing growth of NodeJS, you will get thousands of tutorials and guides for unit tests with node technology.

One of the most popular pattern for developing node modules is the ‘revealing pattern’ , basically it allows you to set private methods and reveal them by a definition (you can read about it in Todd Motto awesome post). Unfortunately one of the disadvantages of this pattern is unit and integration testing complexity. These are my notes using mocha, sinon and chai.

Code examples

for example:

// revealingExample.js
'use strict';
var revealingExample = function(){
    function privateMethod(a,b){
        return (a + b);
    }

    var publicMethod = function(externalObject, callback){
        externalObject.doSometing(privateMethod(1,2), function(err, result){
            if (err) {
                return callback(err);
            }

            callback(null, result);
        });
    };

    return {
        method: publicMethod
    };
};

module.exports = revealingExample;

// otherFile.js
var revealing = require('revealingExample');
...
revealing.method(dbConnection, function(err,result){
    console.log('revealing pattern');
});

And the test will be:

// revealingExmaple_test.js
"use strict";

var sinon = require('sinon');
var chai = require('chai');
chai.should();

describe('revealing example', function(){

    var revealingExample = require('revealingExample')();
    var externalObject = {doSometing: function(){}};
    var data;

    beforeEach(function(done){
        data = [{ id: 2, numbers: '3,2,1' }, { id: 3, numbers: '4,2'}];
        done();
    });

    function verifyResults(mock, data, exceptedData, done) {
        mock.verify();
        mock.restore();
        chai.expect(data).to.eql(exceptedData);
        done();
    }

    describe('publicMethod', function(){
        it('success', function(done){
            var exceptedData = [{id: 2, numbers: [3, 2, 1]}, {id: 3, numbers: [4, 2]}];
            var mock = sinon.mock(externalObject);
            mock.expects('doSometing').withArgs(3).yieldsAsync(null, data);
            revealingExample.getUsersnumbers(externalObject, function(err, data){
                verifyResults(mock, data, exceptedData, done);
            });
        });

        it('error raised', function(done){
            var mock = sinon.mock(externalObject);
            mock.expects('doSometing').withArgs(null).yieldsAsync(new Error('DB error'), null);
            revealingExample.getUsersnumbers(externalObject, function(err, data){
                verifyResults(mock, err.message, 'DB error', done);
            });
        });
    });
});

Lets say you would like to add some integration tests, and lets say revealingExample is the last chain. how would you stub externalObject? keep in mind we are creating new instance of our server and http request, so the creation of revealingExample is performed somewhere down the chain.

First, we will wrap revealingExample:

 //revealingExample.js
module.exports.getInstance = revealingExample;

Now we could stub it:

// revealingExample_integraion_test.js
'use strict';

var server = require('server.js');
var chai = require('chai');
var sinon = require("sinon");
var request = require('request');
chai.should();

describe('http request', function(){
    var port = 3017;
    var domainWithPort = 'http://localhost:' + port;
    function path(relativePath){
        return domainWithPort + relativePath;
    }
    function requestOptions(relativePath){
        return {
            url: path(relativePath),
            headers: {'Content-Type': 'text/plain'}
        };
    }

    var mockData = [{ id: 2, numbers: '3,2,1' }, { id: 3, numbers: '4,2'}];

    var publicMethodStub = function(externalObject, callback){
        callback(null, mockData);
    };

    var revealingExample = require('revealingExample');

    var revealingExampleSpy = sinon.stub(revealingExample, 'getInstance', function(){
        return {
            publicMethod: publicMethodStub
        };
    });

    server.run(port);

    describe('get to /api/v1/something ', function(){
        beforeEach(function(){
            revealingExampleSpy.reset();
        });
        var resourcesTests = [
            {
                url: '/api/v1/something',
                expectedBody: {something: true},
                testName:' get something'
            }
        ];

        resourcesTests.forEach(function(resourcesTest){
            it('should return proper body ' + resourcesTest.testName, function (done) {
                request.get(requestOptions(resourcesTest.url), function (err, res, body) {
                    var expectedBody = resourcesTest.expectedBody;
                    chai.expect(res.statusCode).to.equal(200);
                    chai.expect(JSON.parse(res.body)).to.deep.equal(expectedBody);
                    chai.expect(revealingExampleSpy.calledOnce).to.be.true;
                    done();
                });
            });
        });
    });
});

TL;DR

  1. Revealing pattern is a popular pattern for writing a node module.
  2. One of advantages is the ability to set private methods and members as part of the closure.
  3. Major disadvantage is complexity of tests, especially integration tests mocks/stubs
  4. Mock/Stub is a very powerful tool when writing tests, it allows the developer to isolate code parts.

Enjoy your testing =]

  • Rotem

    Great Post i love it