Search code examples
node.jsunit-testingmockingmocha.jsmockery

Mockery mocking os.type() is not working


I'm trying to unit test a module of mine which is dependent on the underlying OS.

I'm trying to use mockery to mock os.type() to return Linux/Windows_NT based on the different tests fixtures.

I'm using mocha as my unit test framework.

I have a describe section that unit tests functionality A of my module which has 2 describe blocks inside of it: one for Windows and one for Linux.

Setup: var reload = require("require-reload")(require); var module_we_test = reload('...');

var linuxOsMock = {
    type: function () {
        return "Linux";
    }
};

var windowsOsMock = {
    type: function () {
        return "Windows_NT";
    }
};


describe('#the_module_we_test', function() {
    before(function() {
        mockery.enable({
            warnOnReplace: false
        });
    });
    after(function() {
        mockery.disable();
    });
    describe('#windows', function() {
        before(function() {
            mockery.registerMock('os', windowsOsMock);
            module_we_test = reload('...');
        });
        describe('#functionality A', function() {
            it('...', function() {
                /* tests functionality A which is OS dependnet */
                module_we_test.functionalityA();
            });
        });
    describe('#linux & darwin', function() {
        before(function() {
            mockery.registerMock('os', linuxOsMock);
            module_we_test = reload('...');
        });
        describe('#functionality A', function() {
            it('...', function() {
                /* tests functionality A which is OS dependnet */
                module_we_test.functionalityA();
            });
        });
    });
});

The reason I require the module I'm testing before each OS dependent test fixture is because the module is using the OS module once in the global scope to decide some variable values like so:

var os = require('os');
var osType = os.type();

/* decide variable values based on os.type() return value */

so, I think unless I require it again, before each fixture it'll have the originally loaded OS module loaded instead of my mock.

Now, the problem is that it just doesn't replace the OS module in the module, and I have no clue why.


Solution

  • This happens because on top you do var module_we_test = require('...');. This will load the original module and keep it in cache. So even if you mock it using mockery everytime you require it you will get back the cached original module. You need to use mockery first to mock the module and then reload the module under test. The reloaded module will retrieve the mocked module now. You can do this by using the [require-reload][1] module.

    Working example

    describe('#the_module_we_test', function() {
        before(function() {
            mockery.enable({
                warnOnReplace: false
            });
        });
        after(function() {
            mockery.disable();
        });
        describe('#windows', function() {
            var module_we_test;
            before(function() {
                mockery.registerMock("os", windowsOsMock);
                module_we_test = reload("...");
            });
            after(function(){
                mockery.deregisterMock(windowsOsMock);
            });
    
            it("#functionality A", function() {
                /* tests functionality A which is OS dependnet */
                assert.equal(module_we_test.type(), "Windows_NT");
            });
        });
        describe('#linux', function() {
            var module_we_test;
            before(function() {
                mockery.registerMock("os", linuxOsMock);
                module_we_test = reload("...");
            });
            after(function(){
                mockery.deregisterMock(linuxOsMock);
            });
    
            it("#functionality A", function() {
                /* tests functionality A which is OS dependnet */
                assert.equal(module_we_test.type(), "Linux");
            });
        });
    });
    

    Update

    After reading more thoroughly the documentation of mockery, it seems that it provides clean-cache functionality. So instead of using require-reload you only have to set

    before(function() {
        mockery.enable({
            warnOnReplace: false,
            useCleanCache: true
        });
    });
    

    and require normally the module that loads your mocked-module. In the after hook of your os describe you need to call mockery.resetCache

    after(function(){
        mockery.deregisterMock(windowsOsMock);
        mockery.resetCache();
    }); 
    

    I believe this is the best way to tackle this scenario using the mockery module