我正在将我的 JavaScript 测试代码从 Jest 移动到 Mocha。Jest 的一个不错的特性是它会自动为您的模块生成存根。这些存根实现与原始存根相同的 API,但它们的所有函数都返回undefined
. 导出的类也被存根,即它们具有与原始类相同的方法,但返回未定义:
// mymodule.js
var MyClass = function() { this.foo = 42; };
MyClass.prototype.getFoo = function() { return this.foo; };
module.exports = {
myclass: MyClass,
myfunction: function() { return 42; }
};
// __tests__/mymodule-test.js
var mm = require('../mymodule'); // Jest auto-mocks this module.
describe('test', function() {
it('functions and constructors are mocked', function() {
expect(mm.myfunction()).toEqual(undefined); // function is stubbed
var mc = new mm.myclass();
expect(mc.getFoo()).toEqual(undefined); // fn on prototype is stubbed.
});
});
由于各种原因,我正在转向 MochaJS,但我想保留这种存根行为。我可以使用 proxyquire 为模块注入存根。但我需要自己定义存根。
我想要的是一个函数,它接受一个 Node 模块并返回类似于模块的 Jest 自动模拟版本的东西。Jest 的代码在moduleMocker.js中。我已经编写了一些自己的代码来做到这一点(包括在下面)。但这很棘手,感觉不像我应该写的代码。
有这样做的标准库吗?
这是我写的:
// stubber.js
var U = require('underscore');
function replaceFunctionsWithStubs(rootObj) {
var replacedObjs = []; // array of [original, replacement] pairs.
function previousReplacement(obj) {
for (var i = 0; i < replacedObjs.length; i++) {
if (replacedObjs[i][0] == obj) {
return replacedObjs[i][1];
}
}
return null;
}
function replacer(obj) {
var t = typeof(obj);
if (t != 'function' && t != 'object') {
// Simple data.
return obj;
}
// Return previous mock to break circular references.
var prevRep = previousReplacement(obj);
if (prevRep) return prevRep;
if (t == 'function') {
var f = function() {};
replacedObjs.push([obj, f]);
if (!U.isEmpty(obj.prototype)) {
// This might actually be a class. Need to stub its prototype, too.
var newPrototype = replacer(obj.prototype);
for (var k in newPrototype) {
f.prototype[k] = newPrototype[k];
}
}
// Stub any properties the function might have.
for (var k in obj) {
f[k] = replacer(obj[k]);
}
return f;
} else if (typeof(obj) == 'object') {
// TODO: Do I need to handle arrays differently?
var newObj = {};
replacedObjs.push([obj, newObj]);
for (var k in obj) {
newObj[k] = replacer(obj[k]);
}
return newObj;
} else {
return obj; // string, number, null, undefined, ...
}
}
return replacer(rootObj);
}
module.exports = function(m) {
return replaceFunctionsWithStubs(m);
};