びぼーろくっ!

誰かに見せるわけでもないけど、備忘録として。。

今日は直交表に関して勉強しました。

テストロジックに関して知識がなかったので(他の知識も皆無だがw)
今回テスト計画を立てる際に指標になればと思い勉強しました。
数学はてんで駄目なので、分かり易いサイトを探すのに苦労しました。

テストパターンを効率化!直交表でテスト工数を最低限に! | geechs magazine

このサイトを参考に自分で直交表を作ってみます。
元バンドマンっぽくチケット管理システムを考えてみましょう。

f:id:kinachan0725:20170725165824p:plain

ひどい対バンの組み合わせですね。
誰がこんなイベント行くのでしょうか・・・

何のためには置いといて、これらを入力するWEBアンケートがあったとします。
このWEBアンケートのテストを網羅的にやるとなると。。

男性-当日-j-pop
男性-当日ーメタル
...etc

f:id:kinachan0725:20170725170027p:plain

2×3×4=24通りのテストが必要になります。
こんなのやってられません。帰りたいです。

そのため、頭のいいお偉いさんが直交表ということを考え出しました。
これは統計学に基づいて
「不具合が起こる要因のほとんどは2つまでの要因の組み合わせによるものだ」
という結論になったといわれます。これで90%以上はカバーできるとの事。

ならばこれらを使ってテスト工数を削減して、
効率のいいテストを組み立てていきたいと思います。

それでは先ほどのチケット管理システムの例を使って直交表を組み立てていきます。

f:id:kinachan0725:20170725170052p:plain

まずはこんな感じで並べていきます。
これで男性&支払い方法 or 目当ての組み合わせは網羅されました。

f:id:kinachan0725:20170725170128p:plain

続いて女性を入力していきます。
女性&支払い方法 or 目当ての組み合わせも網羅されました。
あとは支払い方法&目当ての組み合わせで足りない所を埋めていくだけですね。

f:id:kinachan0725:20170725170151p:plain

この表だと偏りがあるか分かり辛いのでソートして、Noを振ります。

f:id:kinachan0725:20170725170215p:plain

全12パターンのうち
男性と女性が6パターンずつ
支払い方法が4パターンずつ
目当てが3パターンずつ
綺麗に網羅されていることが分かりますね。

当然、これらの目当てに
性別:中性とか目当てにブルース・R&Dなども増えていくと総当りだとパターン数が増えていきます。
(中性という性別は存在しませんが。。。)
ただ、これらの直交表を使えば複雑なテストほど工数が減っていくので効果的ですね。

mochaのonlyを検知する。

今回の記事もmochaです。
mochaのonlyメソッドは付与されているテストのみ実行する事が出来るので
特定のテストの動作確認をしたい。って時に活用できるかと思います。

ただ、コミット時onlyを削除し忘れて
本来なら失敗するテストも成功してしまい気付かないケースがあります。
そうなるとテストがSkipされてテストで気付けない⇒よって手動実行みたいなケースも想像できます。
となると本末転倒です。。

そこでtest実行時、テスト対象のファイルから.onlyが付与されているものを
エラーとして返してくれるpackageをインストールしました。

dot-only-hunter
GitHub - dasilvacontin/dot-only-hunter: Hunt down `.only`s before it's too late.

まずはインストール。本番環境では不要なのでdevのみにインストールします。

npm install --save-dev dot-only-hunter

その後、package.json内のscriptsの中に下記を追加します。

    "dot-only-hunter":"dot-only-hunter test/**/*.js",
    "test": "dot-only-hunter && NODE_ENV=test mocha --recursive --compilers js:babel-register test/*",

test内で&&で囲み先にdot-only-hunterが実行するようにします。
これでdot-only-hunterが先にtestディレクリ内の.onlyを検知してくれて
onlyが付与されている状態であればテストをしないで失敗します。
skipされるのにテストを全体で実行しても意味がないので。。

対象のソースコード

    describe.only('clacTest', () => {
      it('testCase1',() => {
        testRunAndVerifycation(100,108);
      });

      it('testCase2', () => {
        testRunAndVerifycation(200,216);
      });
    });

実行してみます。

clac.js
f:id:kinachan0725:20170724164117p:plain

きちんと.onlyを検知できているみたいです。
では、.onlyを外してみます。

f:id:kinachan0725:20170724164818p:plain
ちゃんとdot-only-hunterが成功になり、その後のテストコマンドを実行しているのが分かります。

また、今回dot-only-hunterを適用するのはnpm testコマンドのみで
前回の記事でお伝えしたtest-debugのコマンドには適用しません。
describeにonlyを付与した状態でtest-debugを叩きながらテストコードを作成し、
終了後にonlyを削除する。もし忘れてしまったらnpm testで検知してくれるという運用を考えています。

mochaのテスト実行時、VSCodeでES6のコードに埋め込んだブレークポイントを有効にしたい。

結構設定が大変でした。。
やり方を乗せておきます。

インストールされている環境

mocha
babel-register

babel-registerを有効にしないで、そのままmochaコマンドを実行すると
import文などのES6記述で構文エラーが発生します。
上記自体はpackage.jsonのscriptに
babel-registerでコンパイルしてあげればOKですが
例:

"test": "NODE_ENV=test mocha --recursive --compilers js:babel-register test/*",

これをVSCode上で適用したい。。と思ったのが背景でした。

設定は下記のようにしました。

//■launch.json
{
   "version" : "0.2.0",
   "configurations" : [
      {
        "name" : "Run mocha",
        "program" : "${workspaceRoot}/node_modules/mocha/bin/_mocha",
         "request" : "launch",
         "args" : [
            "--compilers",
            "js:babel-core/register",
            "--recursive",
            "--debug-brk",
            "test/**/*.js"
         ],
         "cwd" : "${workspaceRoot}",
         "env" : {
            "NODE_ENV" : "development"
         },
         "protocol" : "inspector",
         "runtimeExecutable" : null,
         "stopOnEntry" : false,
         "type" : "node"
      }
   ]
}

nameはいわずがなでprogramにmochaのファイルを読み込ませます。
argsにmochaに渡す引数オプションを渡しています。
"--compilers","js:babel-core/register"とそれぞれargsに個別に渡していいのね。。
って事をしらなくて小一時間悩みましたw

続いてpackeage.jsonのscriptsの中に下記を追加

"test-debug": "NODE_ENV=test mocha --recursive --debug-brk --compilers js:babel-register test/**/*.js"

今回はtest-debugと命名しましたが何でもいいと思います。(npmコマンドに存在しなければ)
ですが、testコマンドと違って--debug-brkを入れないとコマンドが終了してしまいブレークポイントにヒットしません。

↑これがなくても動きました。。launch.jsonに設定するだけで大丈夫のようです。

これで設定は完了!
まずはターミナルで下記コマンドを実行します。

npm run test-debug

今回test-debugと命名したので、runをつけなければ動きません。
npm上でエイリアスできないものはrunをつけないと動かない?というイメージです。
(ちなみにtstやtもtestコマンドとしてエイリアスされるので、被らないようにしなければいけないっぽい)
詳しく読んでないので文献を載せておきます。意味が違ったらすみません。
test | npm Documentation
run-script | npm Documentation

次にtestディレクトリ配下のjsファイルにブレークポイントを設置して・・・
VSCode上のデバック項目を確認

f:id:kinachan0725:20170721160056p:plain

launch.jsonで命名したRun mochaが反映されています。
再生アイコンを押して実行!

f:id:kinachan0725:20170721160059p:plain

ちゃんとES6上のコードが反映されています。
一件落着!

ES6の記述でmocha + sinon.jsを使ったスタブ化を実施したのでメモ。。

いやー、大変だった。
そもそもES6の記述方法に慣れていないというのもあるけど、
ドキュメントやexsampleをあまり見ないで実施したので今度から見るようにしようw

処理の内容
./data/person.jsonの中にある大量の人物データからidに一致する人物を抽出します。
ただし元データのjsonファイルに一致するIDが存在しない場合、
当然getPersonNameAddGreeting()でもコケます。
今回はgetPersonsの戻りや処理をスタブ化します。
スタブ化されるため、getPersonsの中の処理は走りません。
そのためgetPersonsの中のconsole.log()も出力されません。


test.js

    //必要なモジュールをインクルード
    //アサーションはpower-assertを使用しています。
    import mocha from 'mocha';
    import assert from 'power-assert';
    import sinon from 'sinon';

    //テスト対象をインクルード
    function requireTestTarget(){
      return require('../testTarget/target');
    }

    // ダミーデータを作成します
    function createDummyData () {
      return {
        "id": "b2223",
        "firstName" : "名無しの権兵衛"
      };
    }

    // 対象のメソッドをスタブ化します。
    function modifyStub(stubTarget,methodName){
      return sinon.stub(stubTarget,methodName);
    }

    //テスト実行&検証
    function ExecuteAndVerifycation(expected) {

      //下記メソッドをStub化する(引数には存在しないデータを渡す)
      let person = target.getPersons('aaaaaaaaaaaaaaaaaaaa');

      // テストの実行
      let result = target.getPersonNameAddGreeting(person);
      
      //検証
      assert(result === expected, 'personNameが一致すること');
    }

    let target;

    //mochaにてテストを実行します。
    describe('sinonTest',() => {
      target = requireTestTarget();
      let stubGetPerson = modifyStub(target, 'getPersons');

      it('testCase1_success', () => {
        stubGetPerson.returns(createDummyData());
        ExecuteAndVerifycation('こんにちは。名無しの権兵衛さん');
      });

      it('testCase2_fail',() => {
        stubGetPerson.returns({"id":"fail","firstName":undefined});
        ExecuteAndVerifycation('そんなひとは存在しない!出て行ってくれ!');
      });
    });

target.js(テスト対象ファイル)

    import personData from './data/person.json';

    exports.getPersons = (id) => {
      let returnValue = '';
      personData.forEach((val) => {
        if (id === val.id) returnValue = val;
      });
      //下記のコンソールが流れていたらStub化していない(処理が走ってる為アウト)
      console.log(`ここが通ってたらStub化していない!戻り値が${returnValue}`);
      return returnValue;
    };

    exports.getPersonNameAddGreeting = (person) => {
      if (person.firstName !== undefined) {
        return `こんにちは。${person.firstName}さん`;
      }
      // personに名前がない場合、説教される。
      return 'そんなひとは存在しない!出て行ってくれ!';
    };

Node.jsは奥が深いですネ。。。

C#でのMOCKの基本的な使い方

今MOCKを中心としてソースコードを記載しているのですが
忘れないように基本的な部分を書いてみました。
参考にならないかもしれませんが、備忘録として。

参考例:

//MoqをNUGETからDL後、下記を定義
using Moq;

//定義
var mock = new Mock</*インターフェース*/>();
//定義したMockの関数を設定
//ImplementMethod //ごまかしたい関数
// IsAny Matches any value of the given TValue type
mock.Setup( x=> x.ImplementMethod(It.IsAny</*型*/>()))
	.Returns/*非同期の場合Asyncを追加*/(  /*戻したい型やobject*/);

実装全て

using Moq;

namespace Verification
{
    //実装部分がまだなInterfaceクラス
    public interface IUnfinishedClass
    {
        string ImplementMethod(string num);
    }
    //IUnfinishedClassの処理が必要なメソッド
    public class Call
    {
        public string CallImplement()
        {
            var mock = new Mock<IUnfinishedClass>(); //Mockを定義
            //InterfaceのImplementMethodをラムダで定義。
            //IsAnyを使用するといつでも呼び出せる
            mock.Setup(x => x.ImplementMethod(It.IsAny<string>()))
                .Returns("さようなら");
            var mockMethod = mock.Object; //Setupしたobjectを定義,interfaceが返る
            
            return mockMethod.ImplementMethod("こんにちは"); //実装部分はないが呼び出される
        }
    }
}
出力結果:さようなら