KarateによるAPIのシナリオテスト自動化 #03 Writing Scenarios

# はじめに

[前回](https://qiita.com/takanorig/items/70f14fa6ef024850ba25)は、KarateのExampleの内容を確認していきました。
今回は、実際に独自のテストシナリオを作成してみたいと思います。


# テスト対象の構成

ここでは、シェアリングエコノミーサービスのひとつとして、レンタサイクルをイメージしたサービスのAPIを用意し、それに対して、テストシナリオを作成してみます。
サービスの構成は、以下のようなイメージになります。

![rentacycle-service.png](https://qiita-image-store.s3.amazonaws.com/0/22893/71fd2612-5c3b-28f2-981b-9877c049d5e9.png)

- サービス条件
  - 空き車両の一覧を確認することができます。
  - 既に貸出されている車両は、さらに貸出することはできません。

# テストシナリオ

## シナリオ作成
以下の2つのテストシナリオを作成していきます。

### シナリオ1
単純に、レンタサイクルの車両一覧を取得します。
シナリオというよりは、単にAPIを呼び出しているだけです。

| No | 操作 | 確認内容 |
|:--|:--|:--|
|(1)|レンタサイクルの車両一覧を取得する。|5台の車両が存在すること。|

Karateのシナリオの内容は、以下になります。

```cucumber
Scenario: Get all rentacycles

    Given path '/rentacycles'
    When method get
    Then status 200
        And assert response.size() === 5
```

### シナリオ2
空き車両を確認し、その車両のレンタル／返却を行います。
そのときの車両の状態を、適宜確認するようにします。

| No | 操作 | 確認内容 |
|:--|:--|:--|
|(1)|空き車両の一覧を取得する。|3台の空き車両が存在すること（A001／A002／A003）。|
|(2)|空き車両のひとつ（A001）を選択し、レンタルする。|結果が成功であること。|
|(3)|空き車両の一覧を取得する。|2台の空き車両が存在すること（A002／A003）。|
|(4)|既に貸出されている車両（A001）を、再度レンタルする。|HTTPステータス409でエラーの応答が返ること。|
|(5)|レンタルした車両（A001）を返却する。|結果が成功であること。|
|(6)|空き車両の一覧を取得する。|3台の空き車両が存在すること（A001／A002／A003）。|

Karateのシナリオの内容は、以下になります。

```cucumber
Scenario: Rent / Return the cycle

    # (1)空き車両一覧取得
    Given path '/rentacycles'
        And param available = true
    When method get
    Then status 200
        And assert response.size() === 3
        And match response contains {id: 'A001', rent: false}
        And match response contains {id: 'A002', rent: false}
        And match response contains {id: 'A003', rent: false}

    # (2)レンタル処理
    * def rentId = response[0].id
    * print rentId
    Given path '/rentacycles/rent'
        And request {id: #(rentId)}
    When method post
    Then status 200
        And match response == 
        """
        {
            result : true
        }
        """

    # (3)空き車両一覧取得
    Given path '/rentacycles'
        And param available = true
    When method get
    Then status 200
        And assert response.size() === 2

    # (4)レンタル処理（不可）
    Given path '/rentacycles/rent'
        And request {id: #(rentId)}
    When method post
    Then status 409

    # (5)返却処理
    Given path '/rentacycles/return'
        And request {id: #(rentId)}
    When method post
    Then status 200
        And match response == 
        """
        {
            result : true
        }
        """

    # (6)空き車両一覧取得
    Given path '/rentacycles'
        And param available = true
    When method get
    Then status 200
        And assert response.size() === 3
```


## シナリオ作成時のポイント
今回のシナリオでは、以下のようなところがポイントになります。

```
And match response contains {id: 'A001', rent: false}
And match response contains {id: 'A002', rent: false}
And match response contains {id: 'A003', rent: false}
```
- 「And」で条件を結合しています。
- 「match response contains xxx」で、レスポンスに含まれているデータを確認しています。

また、ここでお気づきでしょうか？

**JSONの変数名の部分は、シナリオファイル内で、直指定しており、クウォート／ダブルクウォートで囲む必要がありません。**
**これにより、可読性も上がりますし、囲む操作が不要な分、変数の指定などが楽なります。地味ですが、便利な仕様だと思います。**
**もちろん、データ部分で文字列の場合は、クウォート／ダブルクウォートで囲んでくださいね。**


```
* def rentId = response[0].id
```
- 直前のレスポンスで、最初の要素の「id」パラメータを取得し、それを変数に格納しています。

```
And request {id: #(rentId)}
```
- 先の内容で変数に保持した値を、「#(rentId)」という記述で、リクエストに指定しています。


# テスト実行
テストに利用するソースコードは、以下に登録してあります。

- https://github.com/takanorig/example-karate

## 事前準備
テスト対象のAPIが必要になりますが、[WireMock](http://wiremock.org/) を利用して、API側をモック化して呼び出せるようにしています。
以下の配下のmainをクラスを起動することで、モックサーバーが立ち上がるようにしています。
IDEなどを利用して、起動してください。

- [/src/mock/java/examples/wiremock/ApiMock.java](https://github.com/takanorig/example-karate/blob/master/src/mock/java/examples/wiremock/ApiMock.java)

## テスト実行
Karateのテストは、以下のコマンドで実行できます。

```console
$ mvn clean test -Dtest=RentaCyclesRunner
```

## テスト結果
テスト結果のレポートは、以下のディレクトリにHTML形式で出力されます。

- target/surefire-reports/TEST-examples.rentacycles.rentacycles.html

APIの内容は、同じAPIでも実行時の状態によって、レスポンスの内容が変わります。
今回のテストでは、その内容が確認できています。

レポートの内容で、リクエスト／レスポンス（青字の部分）は、実行時の内容を確認できるので、エラーが発生している場合などの原因調査にも役立ちます。

![karate-rentacycles.png](https://qiita-image-store.s3.amazonaws.com/0/22893/314c6d4f-69f9-596e-1bd1-8b6641461649.png)

# まとめ

実際に、テストシナリオを作成してみましたが、Karateでは、REST-APIを実行／確認するための便利な機能が豊富に用意されており、大きな手間なく、シナリオを作成できました。
以下のような点は、シナリオを作成するのに役立つ内容ですね。

- リクエスト／レスポンスを処理するための関数／変数がデフォルトで用意されている。
- 変数などを扱うことができ、前のレスポンスのデータを活用することも可能である。
- assertの内容も、柔軟に指定可能である。