JavaScript
テスト
jasmine

jasmineでcsvからテストデータを読み込み、ロジックテスト。

Javascriptのロジックテストが必要になったのですが、大仰な環境を作るコストも時間もなく、要件的に他のライブラリを組み込む事も難しかったので、普通のChromeのみで出来るテスト環境を構築してみました。

1. 前提条件

  • Javascriptのみのロジックテスト(画面操作等は行わない)
  • 他ライブラリは使わない
  • インストールは行わない(テスト用の環境構築不要)
  • WindowsとMacで動く事
  • ブラウザはChrome
  • テストデータと回答はCSVで用意する

2. 選定

上記条件を満たせそうなものとして、JasmineとQUnitが候補にあがりました。

Jasmine vs QUnit detailed comparison as of 2017 - Slant

Qunitの方が学習コストが少なそうでしたが、今後の拡張性を考えてJasmineを選択しました。

3. Jasminの導入

Jasmineオフィシャルサイト
Jasmine使い方メモ - Qiita

スタンドアロン版を導入します

3.1. ダウンロード

GitHubリリースページから、スタンドアロン版zipを取得します

https://github.com/jasmine/jasmine/releases
2017/06/27時点で、2.6.4が最新

3.2. 解凍

適当なフォルダに解凍します

3.3. 構成

解凍直後のファイル構成

C:\hogehoge
│  MIT.LICENSE
│  SpecRunner.html
│  
├─lib
│  └─jasmine-2.6.4
│          boot.js
│          console.js
│          jasmine-html.js
│          jasmine.css
│          jasmine.js
│          jasmine_favicon.png
│          
├─spec … <- テストJavaScript用フォルダ
│      PlayerSpec.js
│      SpecHelper.js
│      
└─src … <- 実処理JavaScript用フォルダ
        Player.js
        Song.js

3.4. Chrome表示

SpecRunner.html をChromeにて表示します

WS000025.JPG

とりあえず、画面表示まで確認できました。
ここから、ロジックスクリプトとテストスクリプトを足していきます。

4. 新規テスト追加

4.1. ロジックスクリプト作成

src\AddLogic.js
function add(a, b) {
    return a + b;
}

足し算して返すだけのスクリプトです。
※エンコードをUTF-8にする事

4.2. テストスクリプト作成

spec\AddTest.js
describe("add テスト", function() {
    it ("add 1+1", function() {
        expect(add(1, 1)).toBe(2);
    });

    it ("add 2+3", function() {
        expect(add(2, 3)).toBe(5);
    });

    // 失敗ケース
    it ("add 6+8", function() {
        expect(add(6, 8)).toBe(10);
    });

});

※エンコードをUTF-8にする事

describe(description, specDefinitions)

  • テストのグループ。この単位でテストが実行される
    • description: テストグループの説明文
    • specDefinitions: テストグループのスクリプト本体

it(description, testFunction, timeout)

  • テストケース
    • description: テストケースの説明文
    • testFunction: テストケーススクリプト本体 (optional)
    • timeout: タイムアウト (optional)

expect(actual).toBe(expected)

  • JUnit.assertEqualみたいなもん?
    • actual: テストロジック
    • expected: 結果

expected the actual value to be === to the expected value.

と公式ドキュメントにあるので、型変換なしで等しい、というテストになる模様。
JavaScript 忘れがちな === と == の違い

toEqual(expected)もあるよ。

4.3. スクリプト実行指定

テスト実行HTMLに、ロジックスクリプトとテストスクリプトを読み込みます。

SpecRunner.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Jasmine Spec Runner v2.6.4</title>

  <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.6.4/jasmine_favicon.png">
  <link rel="stylesheet" href="lib/jasmine-2.6.4/jasmine.css">

  <script src="lib/jasmine-2.6.4/jasmine.js"></script>
  <script src="lib/jasmine-2.6.4/jasmine-html.js"></script>
  <script src="lib/jasmine-2.6.4/boot.js"></script>

  <!-- include source files here... -->
  <script src="src/Player.js"></script>
  <script src="src/Song.js"></script>

  <!-- 足し算ロジックスクリプト -->
  <script src="src/AddLogic.js"></script>

  <!-- include spec files here... -->
  <script src="spec/SpecHelper.js"></script>
  <script src="spec/PlayerSpec.js"></script>

  <!-- 足し算ロジックテストスクリプト -->
  <script src="spec/AddTest.js"></script>

</head>

<body>
</body>
</html>

4.4. テスト実行

Chromeを再起動して、テストを実行します。

WS000025 (2).JPG

ちゃんと失敗しました!

5. テストデータのCSV化

テストデータをCSVから読み込み、それをくるくる回してテストできるようにしていきます。

5.1. テストデータCSV作成

テストデータとロジックの結果をひとまとめにしたCSVを作成します。

WS000027.JPG

ランダムに生成した2つの数値を足した結果を横に並べました。
1か所だけ、わざと手動で間違ったデータを入れています。

TestData.csv
15,94,109
45,85,130
30,65,95
56,98,154
18,80,98
24,86,110
64,70,134
9,97,106
8,98,106
22,32,200
44,26,70
42,12,54
76,1,77
45,61,106
12,53,65
52,68,120
43,13,56
23,11,34
73,29,102
6,32,38
12,57,69

5.2. CSV読み込みスクリプト作成

CSVの結果を読み込み、その結果を数値に変換して、保持します。

src\CsvUtils.js
// CSVを非同期で取得し、その結果を変換して返す
function loadCsv(path) {
    return new Promise( function(resolve) {
        let xhr = new XMLHttpRequest();
        xhr.onload = ()=>{
            resolve(convertCSVtoAddData(xhr.responseText));
        };
        xhr.open("GET", path);
        xhr.send(null);
    });
}

// 読み込んだCSVデータをAddData型の配列に変換する
function convertCSVtoAddData(str) {
        var result = [];

        // 改行でいったん区切る
        var tmp = str.split("\n");

        for(var i = 0; i < tmp.length; ++i){
            // 空行は無視
            if (tmp[i].trim().length > 0) {
                // カンマ区切りで足し算データを生成して設定
                result[i] = new AddData(tmp[i].trim().split(','));
            }
        }

    return result;
}

参考) JavaScriptでCSVファイルを読み込む方法 | UX MILK

src\AddData.js
function AddData(csvdata) {

    // 足し算1つめ: 数値変換
    this.num1 = eval(csvdata[0]);

    // 足し算2つめ: 数値変換
    this.num2 = eval(csvdata[1]);

    // 足し算結果: 数値変換
    this.result = eval(csvdata[2]);

}

5.3. CSVを用いたテストスクリプト作成

spec\AddCsvSpec.js
describe("csv add test async", function(){
    var addDatas;

    // 最初にCSVを読み込んでおく
    beforeAll(function(done){
        // 非同期でCSVを読み込み、読み込みが完了したら、テストを実行する
        loadCsv("../TestData.csv")
            .then(function(datas) {
                addDatas = datas;
                done();
            });
    });

    function itAdd(n) {
        // itのdescriptionは行番号であるため、+1しておく
        it("it :"+ (n+1) , function(){
            if (addDatas.length <= n) {
                // 配列を超えた場合、スルーさせる
                // itの中にexpectがあるとエラーになるため、ダミーテスト
                expect(true).toBe(true);
            }
            else {
                var ad = addDatas[n];

                // 足し算データが取れた場合、テスト
                console.log("it: "+ ad['num1'] + "+"+ ad['num2']);

                // ロジックの結果とCSVデータの結果を比較する
                expect(add(ad['num1'], ad['num2'])).toBe(ad['result']);
            }
        });
    }

    // 適当な数をループする
    for (var n = 0; n < 30; n++) {
        // it は非同期の後に呼ばれるので、function外だししておく
        itAdd(n);
    }

});

※ファイルの読み込み起点は、SpecRunner.htmlからとなります。

SpecRunner.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Jasmine Spec Runner v2.6.4</title>

  <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.6.4/jasmine_favicon.png">
  <link rel="stylesheet" href="lib/jasmine-2.6.4/jasmine.css">

  <script src="lib/jasmine-2.6.4/jasmine.js"></script>
  <script src="lib/jasmine-2.6.4/jasmine-html.js"></script>
  <script src="lib/jasmine-2.6.4/boot.js"></script>

  <!-- 足し算ロジックスクリプト -->
  <script src="src/AddLogic.js"></script>

  <!-- CSV読み込み用スクリプト -->
  <script src="src/AddData.js"></script>
  <script src="src/CsvUtils.js"></script>

  <!-- include spec files here... -->
  <script src="spec/SpecHelper.js"></script>

  <!-- 足し算ロジックCSVテストスクリプト -->
  <script src="spec/AddCsvSpec.js"></script>


</head>

<body>
</body>
</html>

5.4. ローカルファイル読み取り可能な状態でChromeを開く

セキュリティの観点から、通常、Chromeからローカルファイルは読み込めません。
ローカルファイルの読み込みを許可するためには、起動オプションの指定が必要です。

Chrome起動.bat
rem -- ローカルファイルを読み込み可能な状態で開く
rem -- 管理者権限のあるコマンドプロンプトで実行する事
"C:\Program Files\Google\Chrome\Application\chrome.exe" --allow-file-access-from-files

--allow-file-access-from-files

  • ローカルファイルの読み取りを許可する起動オプション

5.5. テストを実行する

WS000029.jpg

無事10行目がエラーになりました。

6. まとめ

CSVファイル読み込み後にテスト、というのが思った以上に大変でした…非同期でのファイル読み込みかつループでテスト、というのが何とも。
本当は、itのdesriptionに足し算式を表示できればよかったのですが、
自分のスキルではどうやっても無理だったので、苦肉の策で行番号を表示しています。
#よい方法をご存じの方、ご教授ください…

参考)