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

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

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

# 2. 選定

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

[Jasmine vs QUnit detailed comparison as of 2017 - Slant](https://www.slant.co/versus/5103/5104/~jasmine_vs_qunit)

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

# 3. Jasminの導入

[Jasmineオフィシャルサイト](https://jasmine.github.io/)
[Jasmine使い方メモ - Qiita](http://qiita.com/opengl-8080/items/cf3acafda9756f4b04c9)

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

## 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](https://qiita-image-store.s3.amazonaws.com/0/184714/86824fce-5d89-4a60-f21e-3a23f0d3f461.jpeg)

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

# 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 忘れがちな　=== と == の違い](http://qiita.com/PianoScoreJP/items/e43d70ec188c6fed73ed)

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](https://qiita-image-store.s3.amazonaws.com/0/184714/2adbd758-a2f9-09f1-9ce9-8b98c00bde18.jpeg)

ちゃんと失敗しました！

# 5. テストデータのCSV化

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

## 5.1. テストデータCSV作成

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

![WS000027.JPG](https://qiita-image-store.s3.amazonaws.com/0/184714/2523b85f-ec05-43a5-3ee4-10be96c51bb6.jpeg)

ランダムに生成した２つの数値を足した結果を横に並べました。
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](http://uxmilk.jp/11586)

```src\AddData.js
function AddData(csvdata) {
	
	// 足し算1つめ: 数値変換
	this.num1 = eval(csvdata[0]);
	
	// 足し算２つめ: 数値変換
	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](https://qiita-image-store.s3.amazonaws.com/0/184714/360c9e2a-6d72-eafb-a191-f8f6d24fcca9.jpeg)

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

# 6. まとめ

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

参考)

 - [Using Loops in Jasmine](http://tosbourn.com/using-loops-in-jasmine/)
 - [Jasmineの非同期テストが1.3から2.0で変わった - Qiita](http://qiita.com/sassy_watson/items/7b3cab6062186d088cdd)
 - [JavaScriptのPromiseオブジェクトについて調べた事 (10) | QUARTETCOM TECH BLOG](http://tech.quartetcom.co.jp/2016/03/22/javascript-promise-10/)