Kotlin Springboot -- Part 20 API E2E で作成したアプリにリクエストしたレスポンスの JSON を key 毎にテストする
Tags: kotlin, ca, gauge
Published: 2022-12-10 15:22:37


# 既存の GET の 200 の確認テストのコードと動作の確認

https://dev.to/kaede_io/kotlin-springboot-part-10-api-e2e-wozuo-ru-3fji

前回レスポンスが 200 返ってくるかどうかのテストは作り終えた。

gauge で kotlin プロジェクトを作る手順に関してはこちら。 

---

## Spec ファイル


```kotlin
# Person API のテスト

## Persons を取得する
* Persons一覧をリクエストする
* "200"が返ってくる
```

src/specs/person.spec

にこのマークダウンでシナリオを書いて

---

## Step の実装ファイル

src/test/kotlin/StepImplementation.kt

にそのシナリオの実装を書く。

Step < Scenario < Spec

この粒度になっている。

```kotlin
    @Step("Persons一覧をリクエストする")
    fun requestPersons() {
        val client = HttpClient.newBuilder().build();
        val request = HttpRequest.newBuilder()
        .uri(URI.create("http://127.0.0.1:8080/persons/"))
            .build();
        val response = client.send(request, HttpResponse.BodyHandlers.ofString());
        ScenarioDataStore.put("StatusCode", response.statusCode())
    }
    @Step("<statusCode>が返ってくる")
    fun shouldBeStatusCode(statusCode: Int) {
        ScenarioDataStore.get("StatusCode") shouldBeEqualTo statusCode
    }
```

localhost8080/persons に HTTP リクエストを送り
データストアに StatusCode というキーでレスポンスのステータスコードを入れる。

そしてデータストアに引数の数値が入っているか確認している。

なお、localhost には HttpClient からリクエストできない。

https://stackoverflow.com/a/35458792

web リクエストをするためだから、HTTP か HTTPS じゃないとだめらしい

https://stackoverflow.com/a/51037827

127 から始まる実際の自分を参照する IP にすれば良いらしい。

なので localhost ではなく http 127.0.0.1 にする

---

## 実行する

https://docs.gauge.org/execution.html?os=linux&language=javascript&ide=vscode

gauge 公式をみると引数が書いてある

```php
mvn test -DspecsDir=specs/persons.spec 
```

spec ファイルのパスを -DspecDir= で指定して mvn test で実行

:line でシナリオも指定できる

```
# Person API のテスト
  ## Persons を取得する  ✔ ✔

Successfully generated html-report to => 
/home/ryo/source/gauge-kotlin/reports/
html-report/index.html

Specifications: 1 executed      1 passed        0 failed        0 skipped
Scenarios:      1 executed      1 passed        0 failed        0 skipped

Total time taken: 467ms
Updates are available. Run `gauge update -c` for more info.
```

1 つシナリオが実行されて
1 つパスして
0 つ落ちて
0 つスキップされた

とでた。なので OK 

---
---










# Body の json を覗いてみる

前回はレスポンスの中のステータスコードだけをみた。

今回はボディに詰まっている JSON データをみる。

そのためにボディの値をみてみる。

```kotlin
println(response.body().toString())
```

とりあえず今回作ったアプリのエンドポイントに対して、レスポンスの body を出してみようとするが、文字列が長すぎるためみれなかった。

example.com のエンドポイントで叩くと

```
(GET http://example.com) 200
<!doctype html>
<html>
<head>
<title>Example Domain</title>
```
 
ちゃんと出る。HTML だから？


```kotlin
    @Step("<json>が返る")
    fun shouldBeJson(json: String) {
        val json = ScenarioDataStore.get("JSON")
        println(json)
        json shouldBeEqualTo "persons.[0]"
    }
```

だが、kluent の shouldBeEqualTo で実際に適当な値でアサートしてみると

```kotlin
: Expected: <persons.[0]> 
but was: <
{"persons":[
{"name":"Taro","age":10},
{"name":"Jiro","age":5},
{"name":"Saburo","age":3}
]}
>
```

みることができた。
これをつかって実際の値をみることができた。

デバックモードで指定の位置で止めて、変数の値を覗く選択肢も有る。

これをうまいこと key:value の map にすればテストできる。

JSONPath を使えばパースできるらしいので試してみる。










---
---

# JsonPathKt を使ってレスポンスのボディの JSON をキー毎にテストする


https://github.com/codeniko/JsonPathKt

Java で有名な JSONPath の Kotlin 版があるらしい。

---

## JsonPathKt を 依存関係に追加して kotlin を 1.5 に更新

```kotlin
        <dependency>
            <groupId>com.nfeld.jsonpathkt</groupId>
            <artifactId>jsonpathkt</artifactId>
            <version>2.0.1</version>
        </dependency>
```

今回は maven の管理なので、POM.xml にこれをいれて実行する

```kotlin
    <properties>
        <kotlin.version>1.5.32</kotlin.version>
    </properties>
```

kotlin の version up も必要なので、1.3 から 1.5 に上げた。

---

## レスポンスの値を確認

```json

{
  "persons": [
    {
      "name": "Taro",
      "age": 10
    },
    {
      "name": "Jiro",
      "age": 5
    },
    {
      "name": "Saburo",
      "age": 3
    }
  ]
}
```

この JSON に対してテストする。

---

## Step の実装として、JsonPath の read でパースして指定位置の値をテストする

```kotlin
    @Step("1人目の<key>が<value>になっている")
    fun shouldNameBeValue(key: String, value: String) {
        val json= ScenarioDataStore.get("JSON") as String
        val parsedJson = JsonPath.parse(json)?.read<String>("$.persons[0].$key")
        parsedJson shouldBeEqualTo value
    }
```

persons の配列 1 つ目の key が value で返ってくるテストの実装を書いた

---

## spec ファイルで呼び出す

```markdown
# Person API のテスト

## Persons を取得する
* Persons一覧をリクエストする
* "200"が返ってくる
* 1人目の"name"が"Taro"になっている
```

---

## 実行する

これを実行すると

```
❯ mvn test -DspecDir=specs/persons.spec


# Person API のテスト
  ## Persons を取得する  ✔ ✔ ✔

Successfully generated html-report to 
=> gauge-kotlin/reports/html-report/index.html
```

ちゃんと通る。

これで key value を指定して、 
spec での呼び出しで API リクエストした、
レスポンスのボディの JSON をテストすることができるようなった。

---

## 1 つめだけでなく、指定の序数もみれるようにする

```kotlin
    @Step("1人目の<key>が<value>になっている")
    fun shouldNameBeValue(key: String, value: String) {
        val json= ScenarioDataStore.get("JSON") as String
        val parsedJson = JsonPath.parse(json)?.read<String>("$.persons[0].$key")
        parsedJson shouldBeEqualTo value
    }
```

いまは決め打ちで 1 人目限定でみてしまっている

```kotlin
    @Step("<number>人目の<key>が<value>になっている")
    fun shouldNameBeValue(number: Int, key: String, value: String) {
        val json= ScenarioDataStore.get("JSON") as String
        val parsedJson = JsonPath.parse(json)?.read<String>("$.persons[${number-1}].$key")
        parsedJson shouldBeEqualTo value
    }
```

1 のとこも変数にして、配列の指定の順番の値もみれるようにした
テストの呼び出しとしては仕様書として、1 つめと書きたいが
プログラムでは配列は 0 始まりなので
実装で -1 してみるようにした。

```markdown
## Persons を取得する
* Persons一覧をリクエストする
* "200"が返ってくる
* "1"人目の"name"が"Taro"になっている
* "2"人目の"name"が"Jiro"になっている
* "3"人目の"name"が"Saburo"になっている
```

これで 3 つ呼び出して

```php
# Person API のテスト
  ## Persons を取得する  ✔ ✔ ✔ ✔ ✔
```

無事に通った。



---
----

# まとめ

API リクエストをした結果のレスポンスをテストする。
そのためには リクエストするステップで ScenarioDataStore に put する。
次にアサート(確認)するステップで ScenarioDataStore から get する。

これで レスポンス の中身をテストできる。

レスポンスのボディの JSON の 指定のキーの値が特定の value になっていることを１つのステップでわかりやすくテストしたい。

そのためには JsonPathKt を使って
ScenarioDataStore から取り出したレスポンスのボディを
パースして、指定のパスの key と value をみる

これで 

```markdown
* Persons一覧をリクエストする
* "200"が返ってくる
* "1"人目の"name"が"Taro"になっている
```

このようにひと目みて、レスポンスの何をテストしているかがわかりやすい
API E2E を作ることができた。

## 伸びしろ

API リクエストをした結果のレスポンスのテストしかして無い。
さらに別の API にリクエストが飛ぶことも、飛んだリクエストをみて、
中身もパースしてテスト擦る必要が有る。

実際にデータをセットアップしてから試す必要も有る。