blog.christoffer.online
Hi, my name is Christoffer and I am an Internet addict.

An easy and fast way on how to mock modules in Node.js

2013-01-20 14:29

Here is a guide on how to quickly (and with minimal effort) mock modules in Node.js for your tests - while in the production environment the correct module is still being fetched.

Below is a very basic example on how to do this.

The function we want to test

Let's say we have a basic module that has a very simple function (isUserBelow30) we want to test.

module.js

var database = require( "./database.js" );

exports.isUserBelow30 = function( username ) {

    var data = database.getUserData( username );

    var age = data.age;

    console.log( "The age of '" + username + "' is '" + age + "'." );

    return age < 30;

};

This module requires the database-module and the function we want to test basically fetches user data from the database and determines if the user is below or above the age of 30. Nothing complicated.

Our test code

To test isUserBelow30 we create have a new file that includes our module.js:

var module = require( "./module.js" );

function test1() {

    var christofferBelow30 = module.isUserBelow30( "christoffer" );
    var natalyaBelow30 = module.isUserBelow30( "natalya" );

    console.log( "Christoffer below 30: " + christofferBelow30 );
    console.log( "Natalya below 30: " + natalyaBelow30 );

    if( !christofferBelow30 && natalyaBelow30 ) {
        console.log( "Test succeeded." );
    } else {
        console.log( "Test failed." );
    }

}

test1();

Also pretty straight forward code. It includes the module.js and based on the data from the database, the function returns true or false depending on if the user is below or above 30.

Why we want to mock the database

How the database.js module looks like and works internally doesn't really matter. It's a black box for us, and for obvious reasons we cannot write tests that will actually fetch data from the database.

If we would, it would take a lot of performance, time, and we might not have the correct data in the database to get try all the different case. It would be much better to mock the data in the database to our isUserBelow30 function.

Making the database mockable

The first thing we need to do is open up module.js, so our tests can hook in and mock the database module.

Inside module.js, we modify this row:

var database = require( "./database.js" );

into this:

var database = exports.database = require( "./database.js" );

This will make the database public from outside the module (thus, allowing us to override it with our mock version). We don't change anything else in this file, meaning the function isUserBelow30 will still use the database variable just as usual:

exports.isUserBelow30 = function( username ) {

    var data = database.getUserData( username );
    
    
    

Creating and adding our mock database

Going back to our test.js, we can now easily mock the database function getUserData, by adding this code:

module.database.getUserData = function( username ) {

    var age = "christoffer" == username ? 50 : 20;

    var data =
        {
            age: age
        };

    return data;

};

By mocking this function (and only this function) of the database module, we can control and simulate how the database module returns.

Running everything would result in:

$ node test.js
The age of 'christoffer' is '50'.
The age of 'natalya' is '20'.
Christoffer below 30: false
Natalya below 30: true
Test succeeded.

Removing the mock and restoring the database

This solution might (depending how you write your tests) pollute modules with mock data, passing it over tot he next test.

To avoid that, it could be an idea to save the original getUserData function and restore it afterwards, like:

var originalGetUserData = module.database.getUserData;

And then later down in your code:

module.database.getUserData = originalGetUserData;

Our final test.js and module.js

test.js

var module = require( "./module.js" );

var originalGetUserData = module.database.getUserData;

module.database.getUserData = function( username ) {

    var age = "christoffer" == username ? 50 : 20;

    var data =
        {
            age: age
        };

    return data;

};

function test1() {

    var christofferBelow30 = module.isUserBelow30( "christoffer" );
    var natalyaBelow30 = module.isUserBelow30( "natalya" );

    console.log( "Christoffer below 30: " + christofferBelow30 );
    console.log( "Natalya below 30: " + natalyaBelow30 );

    if( !christofferBelow30 && natalyaBelow30 ) {
        console.log( "Test succeeded." );
    } else {
        console.log( "Test failed." );
    }

}

test1();

module.database.getUserData = originalGetUserData;

module.js

var database = exports.database = require( "./database.js" );

exports.isUserBelow30 = function( username ) {

    var data = database.getUserData( username );

    var age = data.age;

    console.log( "The age of '" + username + "' is '" + age + "'." );

    return age < 30;

};