はじめに。
モックなどへ差し替えが効く場所と、効かない場所でハマったのでメモ。
それから派生して、UTテストフレームを利用する場合に気にすべき
元コードの設計観点など。
テスト対象ファイル内の関数は差し替え出来ない。
require() されるファイルで定義されたインスタンスは触れないみたい。
sinon でStub化を試みてもダメ。
たとえば、以下のようなソースファイル stub_trial.js のテストにおいて、
a:function(){} を差し替えようとしても、出来ない。
元の関数が呼ばれてしまう。
(カプセル化の観点から考えれば、妥当なんですが)
var a = function( argv ){
console.log( "[a()] is called with \"" + argv +"\"" );
return argv;
}
var z = function( key ){
return a( key );
}
exports.a = a;
exports.z = z;
上記に対するテストコード。
b() をテストするときは、a()をスタブに差し替えを試みているが、NG。
const chai = require("chai");
const assert = chai.assert;
const expect = chai.expect;
const sinon = require("sinon");
const stub_trial = require("../src/stub_trial.js");
describe( "require先の関数をstub化できる?", function(){
describe( "#z()のテスト。内部でa()が呼ばれている", function(){
it(" normal test." ,function(){
expect(
stub_trial.z("test"), "just called."
).to.equal( "test" );
});
it(" stub test.", function(){ // ⇒やっぱ出来ないみたい。
var stub = sinon.stub( stub_trial, "a" );
stub.withArgs("test").returns( "stub!" );
expect(
stub_trial.z("test"), "with stub."
).to.equal( "stub!" );
});
});
});
require() されるファイル一括単位であれば方法はあるみたい?
試してないので詳しくは分らないけど。
対応策⇒差替えたい関数はStrategy パターン にしておく。
UTテストフレームを利用で困らないためには、差替えたい関数には
Strategy パターン を適用しておく。
こうしておくと、差し替えが容易に出来る。
さもないと、困難度が跳ね上がることがあるので注意。
なお、外部環境依存や外部出力などの「副作用を持つ」
関数は差替えた方がUTが楽な場合が多い。
なので、「副作用を持つ関数には、Strategyパターン適用」しておく方針。
イメージとしては以下。
var a_strategy_out_external_effect = function( argv ){
console.log( "[a()] is called with \"" + argv +"\"" );
return argv;
}
var b = function( str ){ return "[" + str + "]"; };
var z1 = function( a_strategy_out_external_effect, key ){
return a( key );
}
var z2 = function( key ){
return b( key );
}
exports.a_strategy_out_external_effect = a_strategy_out_external_effect;
exports.b = b;
exports.z1 = z1;
exports.z2 = z2;
これをテストするコードは以下。
z1() をテストするときは、a_strategy_out_external_effect() をスタブ化出来る。
const chai = require("chai");
const assert = chai.assert;
const expect = chai.expect;
const sinon = require("sinon");
const stub_trial = require("../src/stub_strategy.js");
describe( "require先の関数をstub化できる?with strategy", function(){
describe( "#z()のテスト。内部でa()が呼ばれている", function(){
it(" normal test." ,function(){
expect(
stub_trial.z1( stub_trial.a_strategy_out_external_effect, "test"), "just called."
).to.equal( "test" );
});
it(" stub test.", function(){ // ⇒これならOK。
var stub = sinon.stub( stub_trial, "a_strategy_out_external_effect" );
stub.withArgs("test").returns( "stub!" );
expect(
stub_trial.z1( stub, "test"), "with stub."
).to.equal( "stub!" );
});
});
describe( "#z2()のテスト。内部で、外部への副作用の無いb()が呼ばれている", function(){
it("normal test", function(){
expect( stub_trial.z2("test"), "just called." )
.to.equal( "[test]" );
});
});
});
なお、副作用(コンソールやファイル出力などの外部への影響)が無い
「 b()
」のような関数については、上記の考慮は不要。
普通に上の関数 z2()
からテストすればよい。
要は
「外部環境に依存するヤツで、テスト時にそれを準備するのが手間、
な関数には Strategy パターン を適用しておくと、UTが楽!」
ってことだね。
参考にしたページ
[sinon のメソッド(公式)]
http://sinonjs.org/docs/
[proxyquireでNode.jsのrequireをモックする - ジンジャー研究室]
http://jinjor-labo.hatenablog.com/entry/2015/03/20/140617
[Node.jsでmochaやproxyquireを使った単体テストをやってみたメモ - Qiita]
http://qiita.com/toshihirock/items/67e51303318d5a6030cd