I want to stub node.js built-ins like fs
so that I don\'t actually make any system level file calls. The only thing I can think to do is to pass in fs
An alternative (although I think Noah's suggestion of rewire is better):
Write a wrapper around require
, named requireStubbable
or so. Put this in a module which you configure once, in test setup code. Because node caches result of require, whenever you require the requireStubbable module again, you'd get the same configured function. You could configure it so that any number of modules would be stubbed, all others would be passed on unchanged.
Any module which you'd want to support passing in stubs need to use the requireStubbable
function instead of regular require
though. The rewire module does not have that drawback, and instead gives control to the calling code.
I've never realized, but since the object (or more precisely: object reference) returned by require("fs")
is cached, you could simply do:
const fs = require("fs")
fs.readFile = function (filename, cb) {
cb(null, new Buffer("fake contents"));
};
// etc
When you include this code anywhere, fs.readFile
will be pointing to the above function everywhere.
This works for stubbing out any module that is a mere collection of functions (like most built-in modules). The cases for which it doesn't work if the module returns a sole function. For this, something like rewire
would be necessary.
var fs = require('./myStubFs');
Would seem to be a big improvement. Whatever solution you find will probably involve writing your own stub functions. The ideal solution would be lower level so you don't have to touch all the files you want doing this, e.g. perhaps you want 3rd party libraries stubbed out as well.
Rewire and other stubbing solutions are good if the module under test is the one making calls to fs
itself. However, if the module under test uses a library which uses fs
underneath, rewire and other stubbing solution get hairy pretty quickly.
There is a better solution now: mock-fs
The mock-fs module allows Node's built-in
fs
module to be backed temporarily by an in-memory, mock file system. This lets you run tests against a set of mock files and directories instead of lugging around a bunch of test fixtures.
Example (shamelessly lifted from its readme):
var mock = require('mock-fs');
mock({
'path/to/fake/dir': {
'some-file.txt': 'file content here',
'empty-dir': {/** empty directory */}
},
'path/to/some.png': new Buffer([8, 6, 7, 5, 3, 0, 9]),
'some/other/path': {/** another empty directory */}
});
Take a look at using-stubs, particularly at the require() part.
Leave you module code as you would do normally, eg:
//myApp.js
var fs = require('fs');
fs.readdir(path, function(err, files) {
//Do something.
});
Then, on your tests module (or any unit testing framework), use using-stubs to modify (and even match or validate) the behaviour of fs:
var using = require('using-stubs');
//get a reference to require('fs')
var fs = using.require('fs');
//override behaviour of fs.readdir
using(fs)('readdir').stub(function(path, callback){
//override fs.readdir() logic
var err = null;
var files = [];
// (...)
//mock original behaviour
callback(err, files)
})
//then run the app normally to test it (some frameworks do this for you)
require('myApp')
Now running your test will override the internal behaviour of fs in myApp.js, without needing to change code in either of the components.
You can also do other cool stuff such as validating how many times methods are called, match exact method call parameters or scope, even override behaviour on new Class instances used internally in myApp.js.
Use memfs in-memory filesystem.
I like using rewire for stubbing out require(...) statements
module-a.js
var fs = require('fs')
function findFile(path, callback) {
fs.readdir(path, function(err, files) {
//Do something.
})
}
module-a-test.js
var rewire = require('rewire')
var moduleA = rewire('./moduleA')
// stub out fs
var fsStub = {
readdir: function(path, callback) {
console.log('fs.readdir stub called')
callback(null, [])
}
}
moduleA.__set__('fs', fsStub)
// call moduleA which now has a fs stubbed out
moduleA()