I've been struggling with this now for several hours and I'm not getting anywhere. This is a simplified version of the code I'm working with:
I have a module called 'setup' that internally reads files from the filesystem, and does some stuff. I'm attempting to write some unit tests that use stubs to return fakedata rather than read from the file system. I'm using the sandbox feature of nodeunit since the functions that I am stubbing are not exported.
setup
├── data.js
├── index.js
├── loader.js
├── package.json
└── test
└── test-setup.js
index.js:
var
loader = require( './loader' );
module.exports = function () {
// do stuff
loader( function ( data ) {
console.log( "read from file: " + JSON.stringify( data, null, 2 ));
// do more stuff
return data;
});
};
loader.js:
module.exports = function ( cb ) {
var data = require( './data' );
cb( data );
};
data.js:
module.exports = {
data: {
field1: "value1",
field2: "value2"
}
};
test-setup.js:
var
nodeunit = require( 'nodeunit' ),
sandbox = require( 'nodeunit' ).utils.sandbox,
sinon = require( 'sinon' ),
loader = require( '../loader' ),
setup = require( '..' );
exports[ 'setup file loading' ] = nodeunit.testCase ({
setUp: function ( callback ) {
var
boxModule = { exports: {} },
boxGlobals = {
module : boxModule,
exports : boxModule.exports,
loader : loader,
require : function () { return loader; },
console : console
};
this.fakedata = {
fakedata: {
field1: "value1",
field2: "value2"
}
};
this.sandbox = sandbox( '../index.js', boxGlobals );
callback();
},
tearDown: function ( callback ) {
delete this.sandbox;
callback();
},
'test1: setup by loading the file normally': function ( t ) {
t.ok( setup() === undefined, 'data is undefined - has not loaded yet' );
t.done();
},
'test2: setup using sandbox and loader function replaced with stub': function ( t ) {
var fakedata = this.fakedata;
var returnFakeData = function () {
return fakedata;
};
var stub = sinon.stub( this.sandbox, 'loader', returnFakeData);
t.equal( this.sandbox.module.exports(), this.fakedata, 'returned value is the fakedata from the stub' );
t.done();
}
});
Test 1 passes. Test 2 fails with: AssertionError: returned value is the fakedata from the stub
The log messages show that it has printed the data from the file rather than the fakedata. When I inspect the this.sandbox.loader function in the debugger it is correctly set to the stub.
The second issue is that I had to fake out the require and pass in the loader function with:
require : function () { return loader; },
Otherwise require couldn't find the ./loader. I tried to change directory with process.chdir before the test, but the require still failed even when the cwd was correctly set to the project directory. Obviously faking out require would only work if there is 1 module required, in the real code there are several requires. The only other way I got the ./loader to load was to modify the code to use an absolute path, and editing the code each time to run the test isn't feasible.
I've probably missed something obvious, writing unit tests for module code that loads data asynchronously must be pretty standard stuff. Is sandbox/stubbing the right approach?
Any help much appreciated.