Sinon.JSとは、スタブやモックなどの実装に役立つJavaScriptのライブラリです。スタブを実装するための関数が充実していて、慣れるとテストがサクサク進められると思います。このSinon.JSを使って下記のような関数のスタブを実装しました。実装できたどうかの検証はQUnitで調べました。
(1) 戻り値ありの関数
(2) 引数がコールバックの関数
(3) 別の関数が応答となる関数
関連記事:
QUnitを用いた単体テストの実装例
導入
下記のSinon.JS公式ページより、「sinon-1.9.0.js」をダウロードします。
フォルダ構成は下記のようにしました。「fuga.js」はテスト対象の関数を記述し、「sinon-stub-test.js」にテストコードのサンプルを記述します。「sinon-stub-test.html」はスタブの検証結果を表示します。
フォルダ構成
/ ∟qunit ∟qunit-1.14.0.css ∟qunit-1.14.0.js ∟sinon ∟sinon-1.9.0.js ∟test ∟hoge.js ∟sinon-stub-test.html ∟sinon-stub-test.js
sinon-stub-test.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Sinon Stub Test</title> <!-- QUnitレポート用CSSをインポート --> <link rel="stylesheet" href="../qunit/qunit-1.14.0.css"> <!-- QUnitをインポート --> <script type="text/javascript" src="../qunit/qunit-1.14.0.js"></script> <!-- Sinon.JSをインポート --> <script type="text/javascript" src="../sinon/sinon-1.9.0.js"></script> <!-- テスト対象コードをインポート --> <script type="text/javascript" src="./fuga.js"></script> <!-- テストコードをインポート --> <script type="text/javascript" src="./sinon-stub-test.js"></script> </head> <body> <!-- テスト結果表示 --> <div id="qunit"></div> </body> </html>
(1) 戻り値ありの関数
下記の2つの関数をスタブとして作ります。
・引数なし関数(fuga1)
・引数あり関数(fuga2)
まず、対象オブジェクトの関数をスタブとするコードを下記のように書きます。
var stub = sinon.stub(object, "method");
戻り値は下記のように記述します。
stub.returns(obj);
引数は、
stub.withArgs(arg1[, arg2, ...]);
で指定でき、この引数に対応する戻り値を例えば下記のように記述できます。
stub.withArgs(true).returns(false); // 引数がtrueならばfalseを返却
fuga.js
// ----- テスト関数定義 ----- var Fuga = function() {}; // 引数なし関数 Fuga.prototype.fuga1 = function() { // 何か処理 return 0; }; // 引数あり関数 Fuga.prototype.fuga2 = function(value) { // 何か処理 return 0; };
sinon-stub-test.js
var fuga; module("Fugaテスト", { // テスト準備 setup: function () { fuga = new Fuga(); } }); // テスト開始 test("引数なし関数", function () { // スタブ設定 var stub = sinon.stub(fuga, "fuga1"); stub.returns(1); // 1を返却 // 検証 ok(fuga.fuga1() == 1, "OK"); // OK }); test("引数なし関数", function () { // スタブ設定 var stub = sinon.stub(fuga, "fuga2"); stub.withArgs(1).returns(10); // 引数が1ならば10を返却 stub.withArgs(2).returns(20); // 引数が2ならば20を返却 // 検証 ok(fuga.fuga2(1) == 10, "OK"); // OK ok(fuga.fuga2(2) == 20, "OK"); // OK });
(2) 引数がコールバックの関数
次のコールバック関数を引数とする関数のスタブを考えます。
・引数なしのコールバック関数(fuga3)
・引数ありのコールバック関数(fuga4)
引数なしのコールバック関数は下記メソッドで呼び出すことができます。
stub.callsArg(index);
indexは引数の番号で、第一引数であれば0、第二引数であれば1を指定します。引数なしのコールバック関数は、
stub.callsArgWith(index, arg1, arg2, …);
で呼び出すことができます。arg*の部分にコールバック関数に与える引数を指定します。似たようなメソッドにyields、yieldsOn等があります(これらはindexの指定が省略されたメソッドのようです)。
fuga.js
// ----- テスト関数定義 ----- var Fuga = function() {}; // 引数なしのコールバック関数 Fuga.prototype.fuga3 = function(callbackFunc) { // 何か処理 callbackFunc(); }; // 引数ありのコールバック関数 Fuga.prototype.fuga4 = function(callbackFunc) { // 何か処理 callbackFunc(0); };
sinon-stub-test.js
var fuga; module("Fugaテスト", { // テスト準備 setup: function () { fuga = new Fuga(); } }); test("引数なしのコールバック関数", function () { // スタブ設定 var stub = sinon.stub(fuga, "fuga3"); stub.callsArg(0); // 第一引数(indexは0)のコールバック関数を呼び出す // 検証用コールバック関数の準備 var result = 0; var callbackResult = function () { result = 1; }; // 検証 fuga.fuga3(callbackResult); ok(result == 1, "OK"); // OK }); test("引数ありのコールバック関数", function () { // スタブ設定 var stub = sinon.stub(fuga, "fuga4"); stub.callsArgWith(0, 10); // 第一引数(indexは0)のコールバック関数(引数は10)を呼び出す // 検証用コールバック関数の準備 var result = 0; var callbackResult = function (value) { result = value; }; // 検証 fuga.fuga4(callbackResult); ok(result == 10, "OK"); // OK });
(3) 別の関数が応答となる関数
例えば通信処理などのように、要求(リクエスト)を行う関数と応答(レスポンス)を受け取る関数が分かれていることがあります。この場合、要求関数が呼ばれた場合、応答関数を呼び出すようなスタブを作れば良さそうです。Sinonでは下記のコードで実現できます。
var stub = sinon.stub(Object obj, String methodName, Function func)
methodNameが呼ばれたら、funcを呼び出すという処理です。
fuga.js
// ----- テスト関数定義 ----- var Fuga = function() { var _result_fuga5 = 0; }; // 要求関数(応答はfuga5_2) Fuga.prototype.fuga5_1 = function() { // 何か処理 // 後続の何かを要求して終わり }; // 応答関数(要求はfuga5_1) Fuga.prototype.fuga5_2 = function(result) { // 何か処理 _result_fuga5 = result; }; // 結果取得関数 Fuga.prototype.getResultFuga5 = function() { return _result_fuga5; };
sinon-stub-test.js
var fuga; module("Fugaテスト", { // テスト準備 setup: function () { fuga = new Fuga(); } }); test("別の関数が応答となる関数", function () { // 擬似要求処理関数 var piyo = function() { fuga.fuga5_2(10); //応答関数 } // スタブ設定 var stub = sinon.stub(fuga, "fuga5_1", piyo); //fuga5_1が呼ばれたら、piyoを呼ぶように設定 // 検証 fuga.fuga5_1(); ok(fuga.getResultFuga5() == 10, "OK"); // OK });