如何在testing过程中存储node.js内置的fs?

我想要像fs一样存留node.js内buildfs这样我实际上不会进行任何系统级别的文件调用。 我唯一能做的就是把fs和所有其他的内build函数作为parameter passing给我所有的函数,以避免使用真正的fs。 这似乎有点愚蠢,并创build一个拥有内置的作为参数的详细function签名。

 var fs = require('fs'); function findFile(path, callback) { _findFile(fs, path, callback); } function _findFile(fs, path, callback) { fs.readdir(path, function(err, files) { //Do something. }); } 

然后在testing过程中:

 var stubFs = { readdir: function(path, callback) { callback(null, []); } }; _findFile.(stubFs, testThing, testCallback); 

有比这更好的方法吗?

我喜欢使用rewire来剔除需要(…)语句

模块正在testing中

模块a.js

 var fs = require('fs') function findFile(path, callback) { fs.readdir(path, function(err, files) { //Do something. }) } 

testing代码

模块-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() 

另外一个select(虽然我认为诺亚的rewire的build议更好):

写一个包装require ,名为requireStubbable左右。 把它放在你configuration一次的模块中,在testing设置代码中。 由于节点caching了require的结果,所以无论何时需要requireStubbable模块,都会得到相同的configuration函数。 您可以对其进行configuration,以便将任意数量的模块进行存根,其他所有模块将保持不变。

尽pipe你希望支持在存根中传递的任何模块都需要使用requireStubbable函数,而不是常规的require 。 rewire模块没有这个缺点,而是控制调用代码。

4月26日新增

我从来没有意识到,但由于require("fs")返回的对象(或更准确地说:对象引用require("fs")被caching,你可以简单地做:

 fs = require("fs") fs.readFile = function (filename, cb) { cb(null, new Buffer("fake contents"); }; // etc 

当你在任何地方包含这个代码时, fs.readFile将指向上面的函数。 这适用于存储任何仅仅是function集合的模块(像大多数内置模块)。 如果模块返回唯一的function,则无法使用的情况。 为此,像rewire这样的东西是必要的。

如果被测模块是自己调用fs的模块,则rewire和其他stubbing解决scheme是很好的。 但是,如果被测模块使用了一个使用fs的库,rewire和其他stubbing解决scheme很快就会变得毛茸茸的。

现在有一个更好的解决scheme: mock-fs

mock-fs模块允许Node内置的fs模块暂时由内存中的模拟文件系统提供支持。 这使您可以对一组模拟文件和目录运行testing,而不是在一堆testing装置上拖拽。

例子(无耻地从其自述中解除):

 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 */} }); 

这是我如何看待这个:

你这样做的方式是明显的第一步,但它吸收必须在任何地方通过这些东西 – 你的函数的调用者不应该在乎你想要testing嘲笑。 您不想只覆盖或猴子补丁全局名称空间中的全局模块为您的testing。 而正常的dependency injection模型在Javascript中是相当冗长的,因为没有本地的范围。

所以在整个模块中,我做了像(function(fs, net, http) { … })(fs, net, http);

然后在这个模块里面,如果有一个类的构造函数,可以将mocks可选的额外参数作为构造函数(或者一个mocks对象参数的可能属性),然后你的testing通过模拟。 您的构造函数只覆盖模块的本地范围内的真实节点模块。

或者,如果模块只有静态function, 有一个这样的函数来初始化模拟,你可以validation你的prod代码中没有调用该函数。

看看使用桩 ,特别是在require()部分。

通常那样留下模块代码,例如:

 //myApp.js var fs = require('fs'); fs.readdir(path, function(err, files) { //Do something. }); 

然后,在您的testing模块(或任何unit testing框架)上,使用使用桩来修改(甚至匹配或validation) 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') 

现在运行testing将覆盖myApp.jsfs的内部行为,而无需更改任何组件中的代码。

你也可以做其他很酷的事情,例如validation调用方法的次数,匹配确切的方法调用参数或范围,甚至覆盖myApp.js中内部使用的新类实例的行为。

看看模拟fs和假fs,这已经做了很多。

使用memfs内存中的文件系统。

存根是模拟组件/模块行为的函数/程序。 存根提供了对testing用例期间函数调用的解答。

一个例子可以是写一个文件,而不需要实际上这样做。

 var fs = require('fs') var writeFileStub = sinon.stub(fs, 'writeFile', function (path, data, cb) { return cb(null) }) expect(writeFileStub).to.be.called writeFileStub.restore() 
 var fs = require('./myStubFs'); 

似乎是一个很大的改进。 无论你发现什么解决scheme都可能涉及编写你自己的存根函数。 理想的解决scheme将是较低的水平,所以你不必触摸所有的文件,你想这样做,例如也许你想第三方库也被淘汰。