如何testing一个函数是否调用特定的方法/函数?

摩卡里有没有一种方法来testing一个函数是否调用特定的方法或外部函数?

我正在使用摩卡与柴,但我打开任何其他断言库。


好的,用sinon来testing一个方法是否被调用是非常容易的。 我不确定testing,看是否正在调用一个外部函数。 所以我更新了一些例子来代表一些更“真实的世界”。 我正在研究节点应用程序,所以foo.jsbar.js都是模块。

例:

foo.js

 var bar = require('bar'); var xyz = function () {}; var Foo = module.exports = function () { this.bar(); bar(); xyz(); }; Foo.prototype.bar = function () {}; 

bar.js

 var bar = module.exports = function () {}; 

fooSpec.js

 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 () { // ???????????? }); }); 

所以你可以看到我已经想出了如何testingFoo.prototype.bar ,但我找不到实现第二和第三个testing的方法。

所以这个问题真的是二合一。

首先 ,“如何testing一个方法是否被调用”:我在这个例子中给出了这个代码,但是基本上,使用sinon.js,你只需要用一个“间谍”来包装这个方法,期待那间谍被召唤。

其次 ,“如何testing一个私有函数(不是作为模块的一部分导出的函数)是否被称为”:

基本上,你没有。 在testing环境中而不是在生产环境中导出这些函数是可能的,但是这对我来说似乎有些过于黑暗。

我得出的结论是,当调用另一个模块时,你应该打破TDD循环,而不是testing这个模块,因为它可能只是less量的代码,模块已经被自己testing了。

如果你正在调用在你模块中声明的私有函数,并且想要testing它,你应该写一个更广泛的testing来testing被调用的函数的结果,而不是testing函数是被调用的还是实际的在function内发生。

这是一个非常简单的例子:

foo.js

 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' } }; 

fooSpec.js

 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'); }); }; 

我正在使用expectMocha断言库,但Chai可能有类似的方法


第一

你可以testing一个函数是否使用Spies调用特定的方法/函数。 你在上面的代码中做了这个。

第二

您正在testing的代码的问题是上下文。 所以我会在这个答案中解决它。 你可以testing一个外部函数是否被调用,但是它需要一个上下文 ,所以你可能不得不改变你的代码。

我以bar (模块)为例。 对于xyz (函数)转到第二种方法。 两者的解释是一样的。

1.在对象内部导出bar

bar.js

 var bar = module.exports = { bar: function () {}; } 

foo.js

 var Foo = module.exports = function () { bar.bar(); .... }; 

这样你就可以窥探它:

fooSpec.js

 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(); 

2.将bar模块设置为Foo的原型方法

如果您不想更改bar.js ,则可以将所需的模块设置为Foo的原型方法。 那么你有一个背景来窥探。

foo.js

 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 

fooSpec.js

 it('should call the module bar immediately', function () { var barSpy = expect.spyOn(Foo.prototype, 'barModule'); new Foo(); expect(barSpy).toHaveBeenCalled(); }); 

说明

你必须做的改变是改变variables的上下文。

要说清楚:

 var bar = require('bar'); var Foo = module.exports = function () { this.bar(); bar(); }; Foo.prototype.bar = function () {}; 

在这个片段中,你需要bar ,稍后使用Foo.prototype设置this.bar 。 那么,如何设置2个相同名称的variables并相互引用呢?

答案是上下文和范围。 你的this.bar引用了在this上下文中设置的barvariables(指向Foo )。 另一方面,你的bar -note没有this – 引用在函数的(模块)范围中设置的barvariables。

所以,你可以testing你的Foo.prototype.bar ,因为它是一个模块方法,有一个上下文,你可以监视它。 买你不能窥探所需的bar因为它的范围(认为它是私人的)。

好读: http : //ryanmorr.com/understanding-scope-and-context-in-javascript/