KarateによるAPIのシナリオテスト自動化 #04 Defining Data

# はじめに
[前回（#03 Writing Scenarios）](https://qiita.com/takanorig/items/2f4963d87af860a60d17)は、モックのサービスを作成して、実際にシナリオテストを実施してみました。

今回は、テストの幅を広げるために、Karateで、どのようにデータの定義をするのかを確認してみます。
テスト実施時に、リクエストの内容を指定したり、レスポンスの内容を確認したりするために、どのようにデータを定義するのか、ということになります。


# データの指定方法

## 変数の指定

まずは、基本的な定義として、変数での指定をしてみます。

```cucumber
Scenario: Normal def

* def text = 'karate example'
* print text
* match text == 'karate example'

* def jatext = '日本語のテキスト'
* print jatext
* match jatext == '日本語のテキスト'
```

- ```def```が、変数の定義です。
- ```print``` は、テスト実行時に、レポートに変数の内容を出力してくれます。
- ```match``` は、値の確認です。

実行したときのレポートの内容は、以下のような感じです。

![karate04_normaldef.png](https://qiita-image-store.s3.amazonaws.com/0/22893/e1f898a4-416b-e97e-63ff-3ac9c2947b4a.png)

問題なく、テストが通っていますね。
当然ながら、日本語も問題なく利用できます。


## JSON形式のデータの指定

次は、JSONデータの指定方法について確認します。
REST-APIのテストに向けては、この辺りの使い勝手は重要ですね。

```cucumber
Scenario: JSON Format

# MultilineでのJSON指定
* def jsonData = 
"""
[
  {name: 'Bob', age: 2},
  {name: 'Wild', age: 4},
  {name: 'Nyan', age: 3}
]
"""
* match jsonData == [{name:'Bob', age:2}, {name:'Wild', age:4}, {name:'Nyan', age:3}]
```

基本的な指定の仕方として、マルチラインでの指定です。
```"""``` で囲むことによって、マルチラインでのデータの指定ができています。


```cucumber
# 値の変更
* set jsonData[0].age = 3
* match jsonData == [{name:'Bob', age:3}, {name:'Wild', age:4}, {name:'Nyan', age:3}]
```

先程指定した ```jsonData``` のデータの一部を変更しています。
```set```コマンドを利用することで、データを変更できます。


```cucumber
# table指定
* table jsonAsTable
    | name   | age |
    | 'Bob'  | 2   |
    | 'Wild' | 4   |
    | 'Nyan' | 3   |

* match jsonAsTable == [{name:'Bob', age:2}, {name:'Wild', age:4}, {name:'Nyan', age:3}]
```

今度は、テーブル形式でのJSONデータの指定です。```table``` コマンドを指定して定義しています。
生のJSONデータよりも、こちらの方が見やすいですね。
ただ、tableの場合、ネストされているようなJSONは定義できません。


```cucumber
# set指定（JSON Pathを意識した指定）
* set jsonAsSetMultipleSyntax
    | path       | 0        | 1      | 2       |
    | name.first | 'John'   | 'Jane' | 'David' |
    | name.last  | 'Smith'  | 'Doe'  | 'Brown' |
    | age        | 20       | 24     |         |

* match jsonAsSetMultipleSyntax[0] == {name: { first:'John', last:'Smith' }, age:20}
* match jsonAsSetMultipleSyntax[1] == {name: { first:'Jane', last:'Doe' }, age:24}
* match jsonAsSetMultipleSyntax[2] == {name: { first:'David', last:'Brown' }}
```

ネストされたJSONデータを定義したい場合は、```set```コマンドを利用することで可能です。
こちらも、テーブル形式に近い内容ですが、```table```のときと比べて、<font color="#ff0000">行と列の指定が入れ替わっていることに注意</font>してください。

テストの実行結果は、以下のようになります。
指定したデータの内容も確認できて、分かりやすいです。

![karate04_jsonformat.png](https://qiita-image-store.s3.amazonaws.com/0/22893/e964a50b-f7e2-6996-599a-3f4f05b70971.png)


## パラメータの置換

上記で、```set```コマンドを利用した変数の値の変換を実施しましたが、指定した変数の一部を置き換えるようなこと（例えば、文字列の一部分を変更した場合など）はできません。

そのような置換としては、別の方法があります。
```replace```コマンドを利用すると、予め置換変数を指定しておき、それを指定した値で置換するようなことが可能です。

```cucumber
Scenario: Replace parameters

# デフォルトの置換変数 <val>
* def text1 = 'hello <foo> world'
* replace text1.foo = 'bar'
* match text1 == 'hello bar world'

# カスタムの置換変数 ${val}
* def text2 = 'hello ${foo} world'
* replace text2.${foo} = 'bar'
* match text2 == 'hello bar world'

# 複数のパラメータの置換
* def text3 = 'hello <val1> world <val2> bye'

* replace text3
    | token | value   |
    | val1  | 'cruel' |
    | val2  | 'good'  |

* match text3 == 'hello cruel world good bye'
```

デフォルトの変数は、```<val>``` という形式です。この場合、```変数.val = 置換後の値```` のようなかたちで、変数の一部を置換できます。

置換パラメータは、形式をカスタマイズすることも可能です。```${val}``` のように定義して、置換することも可能です。この場合は、```変数.${val} = 置換後の値```` というかたちで置換します。
私は、どちらかというと、こちらの方が好みです。置換パラメータを指定する際に、こちらの方がパッと見た目で分かりやすい。

さらに、テーブル形式で、複数の置換パラメータを、まとめて置換することもできます。

![karate04_replace.png](https://qiita-image-store.s3.amazonaws.com/0/22893/726039e1-4246-2267-717b-def7230cd5ec.png)


## ファイルからの読み込み

データの内容が大きい場合など、featureファイルの中で指定するのは、手間かもしれません。
そういう場合には、ファイルを読み込んで、利用することもできます。

```cucumber
Scenario: Reading files

* def sample01Json = read('sample01.json')
* print sample01Json

* match sample01Json == '#[7]'
* match sample01Json[0] == {color:'red', value:'#f00'}
* match sample01Json[6] == {color:'black', value:'#000'}
```

たった、これだけです。ファイルの読み込みも簡単ですね。
ただし、指定したファイルは、Karateを実行する際のクラスパスに含まれている必要があります。

![karate04_readingfiles.png](https://qiita-image-store.s3.amazonaws.com/0/22893/07eb45e1-1f87-b0d7-8957-ea4d22d790f2.png)


# まとめ
Karateのデータの定義方法を確認してみましたが、豊富な指定の仕方があり、また、それぞれほぼコマンドひとつで実現されており、簡単に定義できるのが良いと思っています。
テストシナリオを作成する上で、これらが変数などが簡単に指定できるのは、作業が効率的に進められるとお見ます。

より、詳しい使い方を調べる場合は、以下の Karate Demo を参照すると良いでしょう。
https://github.com/intuit/karate/tree/master/karate-demo