Mocha 有没有办法测试函数是否调用特定方法或外部函数?
我正在使用 Mocha 和 Chai,但我对任何其他断言库持开放态度。
好的,所以使用 sinon 来测试方法是否被调用是非常容易的。我不确定是否要测试是否正在调用外部函数。因此,我更新了示例以代表一些更“真实的世界”。我正在开发一个节点应用程序,因此
foo.js
和 bar.js
都是模块。
##示例:
var bar = require('bar');
var xyz = function () {};
var Foo = module.exports = function () {
this.bar();
bar();
xyz();
};
Foo.prototype.bar = function () {};
var bar = module.exports = function () {};
var chai = require('chai');
var sinon = require('sinon');
var sinonChai = require('sinonChai');
var expect = chai.expect;
var Foo = require('../lib/foo');
chai.use('sinonChai');
describe('Foo', function () {
var method;
beforeEach(function (done) {
method = sinon.spy(Foo.prototype, 'bar');
done();
});
afterEach(function (done) {
method.restore();
done();
});
it('should call Foo.prototype.bar() immediately', function () {
new Foo();
expect(method).to.have.been.called;
});
it('should call the module bar immediately', function () {
// ????????????
});
it('should call xyz() immediately', function () {
// ????????????
});
});
正如你所看到的,我已经弄清楚了如何测试
Foo.prototype.bar
,但我找不到实现第二个和第三个测试的方法。
所以这个问题确实是二合一的。
首先,“如何测试方法是否被调用”: 我在示例中为此列出了代码,但基本上,使用 sinon.js,您只需将该方法包装在“间谍”中,这允许您编写一个期望调用该间谍的测试。
其次,“如何测试私有函数(未作为模块的一部分导出的函数)是否已被调用”:
基本上,你不需要。在测试环境中而不是在生产环境中导出这些函数是可能的,但这对我来说似乎有点太老套了。
我得出的结论是,当调用另一个模块时,您应该打破 TDD 周期而不是对此进行测试,因为它可能只是少量代码,并且该模块已经自己进行了测试。
如果您正在调用模块内声明的私有函数并且想要测试它,您应该编写一个更广泛的测试来测试调用该函数的结果,而不是测试该函数是否正在被调用或者是什么实际上发生在函数内。
这是一个非常简单的例子:
var _ = require('lodash');
var Foo = module.exports = function (config) {
this.config = _.merge({
role: 'user',
x: '123',
y: '321'
},
config);
this.config.role = validateRole(this.config.role);
};
var validateRole = function (role) {
var roles = [
'user', 'editor', 'admin'
];
if (_.contains(roles, role)) {
return role;
} else {
return 'user'
}
};
var chai = require('chai');
var expect = chai.expect;
var Foo = require('../lib/foo');
describe('Foo', function () {
it('should set role to \'user\' if role is not valid', function () {
var foo = new Foo({role: 'invalid'});
expect(foo.config.role).to.equal('user');
});
};
我正在使用
expect
断言库和 Mocha
,但是 Chai
可能有类似的方法
您可以使用 Spies 测试函数是否调用特定方法/函数。您在上面的代码中做到了这一点。
您正在测试的代码的问题是上下文。所以我会在这个答案中解决这个问题。您可以测试是否调用外部函数,但它需要上下文,因此您可能需要更改代码。
我使用
(模块)作为示例。对于bar
(函数),请转到第二种方法。两者的解释是相同的。xyz
bar
var bar = module.exports = {
bar: function () {};
}
var Foo = module.exports = function () {
bar.bar();
....
};
这样你就可以监视它的行为:
it('should call the module bar immediately', function () {
//note I'm getting the bar method from the exported object (bar module)
var bar = expect.spyOn(bar, 'bar');
new Foo();
expect(bar).toHaveBeenCalled();
bar
模块设置为 Foo 的原型方法如果你不想改变
bar.js
,你可以将所需的模块设置为Foo的原型方法。然后你就有了一个可以监视的上下文。
var bar = require('./bar');
var Foo = module.exports = function () {
this.bar();
this.barModule();
};
Foo.prototype.bar = function () {};
Foo.prototype.barModule = bar; // setting here as barModule
it('should call the module bar immediately', function () {
var barSpy = expect.spyOn(Foo.prototype, 'barModule');
new Foo();
expect(barSpy).toHaveBeenCalled();
});
您必须执行的更改是为了更改变量的上下文。
为了说清楚:
var bar = require('bar');
var Foo = module.exports = function () {
this.bar();
bar();
};
Foo.prototype.bar = function () {};
在此代码片段中,您需要
bar
并稍后使用 this.bar
设置 Foo.prototype
。那么,如何设置两个同名变量并很好地相互引用呢?
答案是背景和范围。您的
this.bar
正在引用 bar
上下文中设置的 this
变量(指向 Foo
)。另一方面,您的 bar
- 请注意没有 this
- 正在引用函数(模块)作用域中设置的 bar
变量。
因此,您可以测试您的
Foo.prototype.bar
,因为它是一个模块方法,有一个上下文,您可以监视它。购买后您无法监视所需的 bar
,因为它是有范围的(将其视为私有)。
好读:http://ryanmorr.com/understanding-scope-and-context-in-javascript/