【SAPUI5】チュートリアルを見ながらTest Driven Developmentを体験する (2) QUnit

##Test Drivenでフォーマッターの実装
今回はTest Driven（テストを先に書く方式）でフォーマッターの機能を実装します。フォーマッターとは、データを所定の表示形式になるように整形する機能です。
以下のチュートリアルを参照します。
[Step 2: A First Unit Test](https://sapui5.hana.ondemand.com/#/topic/b81736e0fcb246efb3b0cf0ca422f8fd)
[Step 3: Adding the Price Formatter](https://sapui5.hana.ondemand.com/#/topic/2bf4892527174264808076be32c1e1b0)

「チュートリアルを見ながらTest Driven Developmentを体験する」シリーズの目次は[こちら](https://qiita.com/tami/items/5776597e19337f57fa34)

##QUnitテスト用の設定を確認
今回のテストで使うファイルは、test/unitフォルダの配下に格納されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/8b0b4383-9426-03c2-ca8f-a57c33c6ae29.png)
QUnitテストを実行するときはunitTests.qunit.htmlを実行します。
ここからunitTests.qunit.jsが呼ばれ、さらにAllTests.jsが呼ばれるという構成になっています。テンプレートからアプリを作った場合も同じ構成となります。（ベストプラクティス）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/64c136de-25b8-89b8-4aa7-e54e75336f98.png)

各ファイルの中身を見てみます。
**unitTests.qunit.js**
QUnitを開始しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/856d6207-96ed-9b05-a681-103381079069.png)

**AllTests.js**
テスト対象の機能（formatterなど）を呼ぶjsファイルをdependencyに設定しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/34141446-ff4e-0a38-7b96-021051e57f0f.png)

**model/formatter.js**
Number unitのテストケースが入っています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/d87a2a50-5244-117c-c308-4e58c1c1fa07.png)

**model/models.js**
デバイスモデル作成のテストケースが入っています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/b07e1146-c5ae-67e5-5090-f6c628559825.png)

お気づきのように、テスト用のフォルダ、ファイルは、テスト対象機能と同じ構成になっています。これが慣例だそうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/2a228d0c-ce25-14a1-b64f-32338364d969.png)

###どこに手を入れればいいの？
**AllTests.js**とテスト対象の機能を呼ぶjsファイルに手を入れます。
AllTests.jsはテスト対象のjsファイルを追加した場合だけ変更が必要で、今回は変更しません。

##Test Driven Developmentのステップ
Test Driven Developmentは以下の順序で進みます。
1. テストを書く
2. 機能を実装する
3. テストを実行する
4. 機能をアプリに組み込む

###実装する機能
Priceの色が、金額に応じて変わるようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/bccd882c-a85b-a71b-b0b9-99308f7c2960.png)
####条件
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/a19d85ae-590f-b13d-aea2-138a28bfa0af.png)

##1. テストを書く
仕様が決まったところで早速テストを書きます。

###フォーマッターのガラだけ作成
テスト実行用jsから呼べるように、フォーマッターのガラだけ先に作っておきます。
webapp/model/formatter.js

```javascript
		priceState: function(){
		}
```

###QUnitテストを書く
PriceState用のモジュール（テストの単位）と、priceStateを呼ぶミニ関数を定義します。この関数はこの後、色々な値でテストを実行するときに使います。
assertionでPriceStateから返された値と想定結果を比較し、一致すればOKとなります。

webapp/test/unit/model/formatter.js

```javascript
	QUnit.module("Price State");
	function priceStateTestCase(oOptions){
		// Act
		var sState = formatter.priceState(oOptions.price);
		// Assert
		oOptions.assert.strictEqual(sState, oOptions.expected, "The price state was correct");
	}
```

次に、上記の関数を呼び出してテストを実行します。テストすべき条件をコメントで先に書いておくと良いと思います。

```javascript
	//price < 50: Status is green (Success)
	//price >= 50 and price < 250: Status is normal (None)
	//price >= 250 and price < 2000: Status is orange (Warning)
	//price >= 2000: Status is red (Error)
```

コメントの下にテストを書きます。

```javascript
	//price < 50: Status is green (Success)
	QUnit.test("Should format the products with a price lower than 50 to Success", function(assert){
		priceStateTestCase.call(this, {
			assert: assert,
			price: 42,
			expected: "Success"
		});
	});
```
残りのケースも同様に、目的に合ったpriceと想定結果の組を与えます。

```javascript
	//price >= 50 and price < 250: Status is normal (None)
	QUnit.test("Should format the products with a price of 50 to Normal", function(assert){
		priceStateTestCase.call(this, {
			assert: assert,
			price: 50,
			expected: "None"
		});
	});	

	QUnit.test("Should format the products with a price betweenn 50 and 250 to Normal", function(assert){
		priceStateTestCase.call(this, {
			assert: assert,
			price: 112,
			expected: "None"
		});
	});	
	
	//price >= 250 and price < 2000: Status is orange (Warning)
	QUnit.test("Should format the products with a price betweenn 250 and 2000 to Warning", function(assert){
		priceStateTestCase.call(this, {
			assert: assert,
			price: 798,
			expected: "Warning"
		});
	});	
	
	//price >= 2000: Status is red (Error)
	QUnit.test("Should format the products with a price higher than 2000 to Error", function(assert){
		priceStateTestCase.call(this, {
			assert: assert,
			price: 2001,
			expected: "Error"
		});
	});		
```

###実行結果
PriceSateのテスト結果は全てエラーになりました。まだ機能を実装していないので想定通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/22992592-dfe4-5b5c-346e-6ae1616d624c.png)

##2. 機能を実装する
フォーマッターを実装します。

```javascript
		priceState: function(iPrice){
			if(iPrice < 50){
				return "Success";
			}else if(iPrice >= 50 && iPrice < 250){
				return "None";
			}else if(iPrice >= 250 && iPrice < 2000){
				return "Warning";
			}else{
				return "Error";
			}
		}
```
##3. テストを実行する
実装後に改めてテストします。今回は正常終了します。
これで機能が正しく実装できていることが確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/88c7e018-fcb7-6830-60a6-e4d731ec2cc1.png)

##4. 機能をアプリに組み込む
ObjectNumberのコントロール（priceを出しているところ）に、stateという属性を追加します。ここにpriceStateを設定します。
**view/Worklist.view.xml**

```xml
							<ObjectNumber
								number="{
									path: 'Price',
									formatter: '.formatter.numberUnit'
								}"
								state="{
									path: 'Price',
									formatter: '.formatter.priceState'
								}"
								unit="{Currency}"
								/>
```

###実行結果
Priceの色が金額に応じて変わりました。実装と同時にテストも終わりました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/80f0e497-5d3e-1b52-ee06-f745c9a7cf03.png)

##関連記事
[チュートリアルを見ながらTest Driven Developmentを体験する (1) 概要](https://qiita.com/tami/items/5776597e19337f57fa34)
[チュートリアルを見ながらTest Driven Developmentを体験する (3) OPA5](https://qiita.com/tami/items/9db97e9253fbf713566f)