Mat*_*lor 149 javascript mocking node.js
这是一个简单的例子,说明了我的问题的关键:
var innerLib = require('./path/to/innerLib');
function underTest() {
return innerLib.doComplexStuff();
}
module.exports = underTest;
Run Code Online (Sandbox Code Playgroud)
我正在尝试为此代码编写单元测试.如何在innerLib
不require
完全嘲笑函数的情况下模拟出对该函数的要求?
编辑:所以这是我试图模拟全局需求,并发现它甚至不会这样做:
var path = require('path'),
vm = require('vm'),
fs = require('fs'),
indexPath = path.join(__dirname, './underTest');
var globalRequire = require;
require = function(name) {
console.log('require: ' + name);
switch(name) {
case 'connect':
case indexPath:
return globalRequire(name);
break;
}
};
Run Code Online (Sandbox Code Playgroud)
问题是require
underTest.js文件中的函数实际上没有被模拟出来.它仍然指向全球require
功能.所以我似乎只能underTest.js
在同一个文件中模拟我正在进行模拟的函数.如果我使用全局require
来包含任何内容,即使在我重写本地副本之后,所需的文件仍将具有全局require
引用.
Tho*_*enz 170
你现在可以!
我发布了proxyquire,它将在您测试时覆盖模块内部的全局需求.
这意味着您无需更改代码即可为所需模块注入模拟.
Proxyquire有一个非常简单的api,它允许解析你试图测试的模块,并通过一个简单的步骤传递其所需模块的模拟/存根.
@Raynos是对的,传统上你不得不诉诸不是非常理想的解决方案来实现这一目标或者做自下而上的开发
这是我创建proxyquire的主要原因 - 允许自上而下的测试驱动开发没有任何麻烦.
查看文档和示例,以确定它是否符合您的需求.
Ell*_*ter 112
在这种情况下,更好的选择是模拟返回的模块的方法.
无论好坏,大多数node.js模块都是单例; 需要()相同模块的两段代码获得与该模块相同的引用.
你可以利用这个并使用像sinon这样的东西来模拟所需的项目. 摩卡测试如下:
// in your testfile
var innerLib = require('./path/to/innerLib');
var underTest = require('./path/to/underTest');
var sinon = require('sinon');
describe("underTest", function() {
it("does something", function() {
sinon.stub(innerLib, 'toCrazyCrap').callsFake(function() {
// whatever you would like innerLib.toCrazyCrap to do under test
});
underTest();
sinon.assert.calledOnce(innerLib.toCrazyCrap); // sinon assertion
innerLib.toCrazyCrap.restore(); // restore original functionality
});
});
Run Code Online (Sandbox Code Playgroud)
Sinon 与chai进行了良好的集成以进行断言,我编写了一个模块来将sinon与mocha集成,以便更容易地进行间谍/存根清理(以避免测试污染.)
请注意,underTest不能以相同的方式进行模拟,因为underTest只返回一个函数.
请注意操作require.cache
并注意require.resolve
方法的部分,因为这是秘密武器。
class MockModules {
constructor() {
this._resolvedPaths = {}
}
add({ path, mock }) {
const resolvedPath = require.resolve(path)
this._resolvedPaths[resolvedPath] = true
require.cache[resolvedPath] = {
id: resolvedPath,
file: resolvedPath,
loaded: true,
exports: mock
}
}
clear(path) {
const resolvedPath = require.resolve(path)
delete this._resolvedPaths[resolvedPath]
delete require.cache[resolvedPath]
}
clearAll() {
Object.keys(this._resolvedPaths).forEach(resolvedPath =>
delete require.cache[resolvedPath]
)
this._resolvedPaths = {}
}
}
Run Code Online (Sandbox Code Playgroud)
使用如下:
describe('#someModuleUsingTheThing', () => {
const mockModules = new MockModules()
beforeAll(() => {
mockModules.add({
// use the same require path as you normally would
path: '../theThing',
// mock return an object with "theThingMethod"
mock: {
theThingMethod: () => true
}
})
})
afterAll(() => {
mockModules.clearAll()
})
it('should do the thing', async () => {
const someModuleUsingTheThing = require('./someModuleUsingTheThing')
expect(someModuleUsingTheThing.theThingMethod()).to.equal(true)
})
})
Run Code Online (Sandbox Code Playgroud)
但是... jest 内置了此功能,我建议您使用测试框架来进行测试。