LoginSignup
5
6

More than 5 years have passed since last update.

テストフレームワークを利用する場合に気を付けること(コード設計)

Last updated at Posted at 2016-09-14

はじめに。

モックなどへ差し替えが効く場所と、効かない場所でハマったのでメモ。
それから派生して、UTテストフレームを利用する場合に気にすべき
元コードの設計観点など。

テスト対象ファイル内の関数は差し替え出来ない。

require() されるファイルで定義されたインスタンスは触れないみたい。
sinon でStub化を試みてもダメ。

たとえば、以下のようなソースファイル stub_trial.js のテストにおいて、
a:function(){} を差し替えようとしても、出来ない。
元の関数が呼ばれてしまう
(カプセル化の観点から考えれば、妥当なんですが)

stub_trial.js
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

stub_trial_test.js
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パターン適用」しておく方針。

イメージとしては以下。

stub_strategy.js
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() をスタブ化出来る。

stub_strategy_test.js
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

[モックテストについていくらか調べた]
http://chikuwa-parfait.tumblr.com/post/137752419523/%E3%83%A2%E3%83%83%E3%82%AF%E3%83%86%E3%82%B9%E3%83%88%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E3%81%84%E3%81%8F%E3%82%89%E3%81%8B%E8%AA%BF%E3%81%B9%E3%81%9F

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6