Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/478.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 在测试期间,如何使用stub node.js内置fs?_Javascript_Node.js_Testing - Fatal编程技术网

Javascript 在测试期间,如何使用stub node.js内置fs?

Javascript 在测试期间,如何使用stub node.js内置fs?,javascript,node.js,testing,Javascript,Node.js,Testing,我想要存根node.js内置,比如fs,这样我就不会真正进行任何系统级文件调用。我能想到的唯一一件事就是将fs和所有其他内置函数作为参数传递给我的所有函数,以避免使用真正的fs。这看起来有点傻,创建了一个冗长的函数签名,里面塞满了内置参数 var fs=require('fs'); 函数findFile(路径,回调){ _findFile(fs、路径、回调); } 函数_findFile(fs、路径、回调){ fs.readdir(路径、函数(错误、文件){ //做点什么。 }); } 然后在

我想要存根node.js内置,比如
fs
,这样我就不会真正进行任何系统级文件调用。我能想到的唯一一件事就是将
fs
和所有其他内置函数作为参数传递给我的所有函数,以避免使用真正的fs。这看起来有点傻,创建了一个冗长的函数签名,里面塞满了内置参数

var fs=require('fs');
函数findFile(路径,回调){
_findFile(fs、路径、回调);
}
函数_findFile(fs、路径、回调){
fs.readdir(路径、函数(错误、文件){
//做点什么。
});
}
然后在测试过程中:

var stubFs={
readdir:函数(路径,回调){
回调(null,[]);
}
};
_findFile(stubFs、testThing、testCallback);

有比这更好的方法吗?

我是这样想的:

这样做显然是第一步,但在任何地方传递这些东西都很糟糕——函数的调用者不应该关心你想用mock进行测试。您不希望只为测试覆盖或修补全局命名空间中的全局模块。在Javascript中,正常的依赖项注入模型非常冗长,因为没有类本地作用域

因此,在整个模块中,我做了如下工作:
(函数(fs,net,http){…})(fs,net,http)

然后在模块内部,如果有类构造函数,则将mock作为构造函数的可选额外参数(或单个
mock
对象参数或其他东西的可能属性),然后测试通过mock。构造函数只覆盖模块本地范围内的实际节点模块

或者,如果模块仅具有静态功能;如果有一个这样的函数初始化mock,您可以验证prod代码中没有调用该函数

var fs = require('./myStubFs');
这似乎是一个很大的进步。无论您找到什么解决方案,都可能需要编写自己的存根函数。理想的解决方案是较低的级别,这样您就不必接触所有要执行此操作的文件,例如,您可能还需要删除第三方库。

我喜欢使用for stubing out require(…)语句

被测模块 模块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()
另一种选择(尽管我认为诺亚建议重新布线更好):

围绕
require
编写一个包装,名为
requiresubbable
左右。将其放入一个模块中,在测试设置代码中配置一次。因为节点缓存require的结果,所以每当您再次需要requireStubbable模块时,都会得到相同的配置函数。您可以对其进行配置,以便任意数量的模块都将被存根化,而所有其他模块都将原封不动地传递

任何想要支持传入存根的模块都需要使用
requirestubable
函数,而不是常规的
require
。重新布线模块没有这个缺点,而是控制调用代码

四月二十六日增补 我从未意识到,但是由于
require(“fs”)
返回的对象(或者更准确地说:对象引用)被缓存,因此您可以简单地执行以下操作:

const fs = require("fs")
fs.readFile = function (filename, cb) {
  cb(null, new Buffer("fake contents"));
};
// etc
当您在任何地方包含此代码时,
fs.readFile
将指向上述函数。
这适用于剔除仅仅是函数集合的任何模块(如大多数内置模块)。如果模块返回一个单独的函数,则它不起作用的情况。为此,需要使用类似于重新布线的方法。

检查模拟fs和假fs,它们已经做了很多工作。

看看,尤其是零件

将模块代码保留为正常情况下的,例如:

//myApp.js
var fs=需要('fs');
fs.readdir(路径、函数(错误、文件){
//做点什么。
});
然后,在您的测试模块(或任何单元测试框架)上,使用使用存根修改(甚至匹配或验证)fs的行为:

var using=require('using-stubs');
//获取对require('fs'的引用)
var fs=使用.require('fs');
//fs.readdir的重写行为
使用(fs)('readdir').stub(函数(路径,回调){
//重写fs.readdir()逻辑
var err=null;
var文件=[];
// (...)
//模仿原始行为
回调(错误,文件)
})
//然后正常运行应用程序进行测试(有些框架会为您执行此操作)
require('myApp')
现在运行测试将覆盖myApp.js中的fs的内部行为,而无需更改任何组件中的代码


您还可以做其他很酷的事情,比如验证方法被调用的次数,匹配精确的方法调用参数或范围,甚至覆盖myApp.js内部使用的新类实例的行为

如果正在测试的模块是调用
fs
本身的模块,则重新布线和其他短截线解决方案是好的。然而,如果被测模块使用了一个在下面使用
fs
的库,那么重新布线和其他存根解决方案很快就会出现问题

现在有一个更好的解决方案:

mock fs模块允许节点的内置
fs
模块由内存中的模拟文件系统临时备份。这使您可以针对一组模拟文件和目录运行测试,而不是拖拉一堆测试装置

示例(不知羞耻地摘自自述):


使用内存文件系统。

存根是模拟组件/模块行为的函数/程序。存根为测试用例中的函数调用提供了固定的答案

例如,可以编写一个文件,而实际上不这样做

var fs = require('fs')

var writeFileStub = sinon.stub(fs, 'writeFile', function (path, data, cb) {  
 return cb(null)
})

expect(writeFileStub).to.be.called  
writeFileStub.restore()  

下面是一个与fs.api一起使用的版本:

const fsMock = sinon.mock(fs.promises);
fsMock.expects('readFile').withArgs('test.json').returns(Promise.resolve(Buffer.from('{}')));

const val = await fs.promises.readFile('test.json');

expect(val.toString()).toEqual('{}');
fsMock.verify();

对我来说,不需要模拟/存根文件,我通常用临时f创建一个临时文件
const fsMock = sinon.mock(fs.promises);
fsMock.expects('readFile').withArgs('test.json').returns(Promise.resolve(Buffer.from('{}')));

const val = await fs.promises.readFile('test.json');

expect(val.toString()).toEqual('{}');
fsMock.verify();