関数が特定のメソッドまたは外部関数を呼び出すかどうかをテストする方法はモカにありますか?
MochaをChaiで使用していますが、他のアサーションライブラリを使用できます。
OK、メトイドが呼び出されているかどうかのテストは、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
をテストする方法はわかりましたが、2番目と3番目のテストを実装する方法が見つかりません。
したがって、この質問は実際には2つの質問でした。
最初に、「メソッドが呼び出されているかどうかをテストする方法」:この例のコードをレイアウトしましたが、基本的に、sinon.jsを使用して、メソッドを「スパイ」にラップするだけです。そのスパイが呼び出されたことを期待するテストを書くことができます。
2番目に、「プライベート関数(モジュールの一部としてエクスポートされなかったもの)が呼び出されたかどうかをテストする方法」:
基本的にはありません。本番環境ではなくテスト環境でこれらの関数をエクスポートすることは可能ですが、これは私には少々ハックしすぎるようです。
別のモジュールを呼び出すときは、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
(関数)の場合は、2番目の方法に進みます。説明はどちらも同じです。
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
を後で設定する必要があります。では、どうすれば同じ名前の2つの変数を設定し、お互いをうまく参照できますか?
答えはコンテキストとスコープです。 this.bar
は、bar
コンテキストセット(this
を指す)であるFoo
変数を参照しています。一方、bar
-this
がないことに注意してください-関数の(モジュール)スコープで設定されたbar
変数を参照しています。
それで、あなたはあなたのFoo.prototype.bar
をテストするかもしれません、それはモジュールメソッドであり、コンテキストを持っていて、あなたはそれをスパイするかもしれないからです。必要なbar
はスコープ付きであるため、スパイすることはできません(プライベートと考えてください)。
よく読んでください: http://ryanmorr.com/understanding-scope-and-context-in-javascript/