我正在努力围绕我的节点模块编写高质量的测试。问题是需要模块系统。我希望能够检查某个所需的模块是否有方法或其状态是否已更改。似乎有两个相对较小的库可以在这里使用:node-gently和mockery。然而,由于他们的低调,这让我认为要么人们没有测试这个,要么有另一种我不知道的方法可以做到这一点。
模拟和测试所需模块的最佳方法是什么?
---------- 更新 ---------------
node-sandbox 的工作原理与下面所述相同,但包含在一个很好的模块中。我发现合作非常愉快。
-------------- 详细的遮阳篷 ---------------
经过多次试验,我发现在模拟事物的同时单独测试节点模块的最佳方法是使用 Vojta Jina 的方法在具有新上下文的虚拟机内运行每个模块,如here所述。
使用此测试虚拟机模块:
var vm = require('vm');
var fs = require('fs');
var path = require('path');
/**
* Helper for unit testing:
* - load module with mocked dependencies
* - allow accessing private state of the module
*
* @param {string} filePath Absolute path to module (file to load)
* @param {Object=} mocks Hash of mocked dependencies
*/
exports.loadModule = function(filePath, mocks) {
mocks = mocks || {};
// this is necessary to allow relative path modules within loaded file
// i.e. requiring ./some inside file /a/b.js needs to be resolved to /a/some
var resolveModule = function(module) {
if (module.charAt(0) !== '.') return module;
return path.resolve(path.dirname(filePath), module);
};
var exports = {};
var context = {
require: function(name) {
return mocks[name] || require(resolveModule(name));
},
console: console,
exports: exports,
module: {
exports: exports
}
};
vm.runInNewContext(fs.readFileSync(filePath), context);
return context;
};
可以使用自己的上下文来测试每个模块,并轻松消除所有外部依赖项。
fsMock = mocks.createFs();
mockRequest = mocks.createRequest();
mockResponse = mocks.createResponse();
// load the module with mock fs instead of real fs
// publish all the private state as an object
module = loadModule('./web-server.js', {fs: fsMock});
我强烈推荐这种方式来单独编写有效的测试。只有验收测试才应该影响整个堆栈。单元和集成测试应该测试系统的隔离部分。
我觉得嘲讽模式很好。也就是说,我通常选择将依赖项作为参数发送给函数(类似于在构造函数中传递依赖项)。
// foo.js
module.exports = function(dep1, dep2) {
return {
bar: function() {
// A function doing stuff with dep1 and dep2
}
}
}
测试时,我可以发送模拟、空对象,无论什么看起来合适。请注意,我不会对所有依赖项执行此操作,基本上仅对 IO 执行此操作 - 我觉得不需要测试我的代码是否调用
path.join
或其他内容。
我认为“低调”让你紧张有以下几个原因:
简而言之,如果您认为嘲笑适合您,那就去做吧!
您可以使用“a”轻松模拟 require:https://npmjs.org/package/a
//Example faking require('./foo') in unit test:
var fakeFoo = {};
var expectRequire = require('a').expectRequire;
expectRequire('./foo').return(fakeFoo);
//in sut:
var foo = require('./foo'); //returns fakeFoo