KarateによるAPIのシナリオテスト自動化 #01 Quick Start

# はじめに
最近のシステム開発では、多様なデバイス／UIへの対応やマイクロサービスなどに対するニーズから、APIを中心とした「APIファースト（API-First）」な開発が増えてきています。
そのような中、当然ながらテストもAPIファーストに対応することになるのですが、API単体でテストするだけでなく、シナリオとしてテストしたいというケースが増えてきて、良いツールがないかと探していたところ、 @shimashima35 さんから、Karateを教えて頂きました。
プロジェクトで導入したところ、導入も比較的簡単で、使い勝手が良かったので、使い方をまとめたいと思います。

# Karateとは？
[Karate](https://github.com/intuit/karate)は、[Cucumber](https://cucumber.io/)というBDD（ビヘイビア駆動開発）を行うためのフレームワークをJavaVM上に移植したもので、Gherkinの文法を自然言語に近い形で記述できるユニットテストツールです。

本家のサイトでは、以下のように紹介されています（日本語訳）。

>Karateは、あらゆる種類のWebサービスへの一連の呼び出しをスクリプト化し、レスポンスが期待値どおりであるかを検証します。 
>複雑なリクエスト・ペイロードを作成し、レスポンス内のデータを走査し、レスポンスから次のリクエストにデータをチェーンすることも、簡単に行うことができます。 Karateのペイロード検証エンジンは、空白やデータ要素が実際に表示される順序の影響を受けずに、2つのJSONまたはXMLドキュメントの「スマートな比較」を行うことができます。また、選択したフィールドを無視することもできます。
>Karateは、Cucumber-JVM上に構築されており、標準Javaプロジェクトのようにテストを実行してレポートを生成できます。 しかし、Javaの代わりに、HTTP、JSON、またはXMLを扱うように設計された言語でテストを記述します。これは、シンプルな方法です。
>
>https://github.com/intuit/karate#web-services-testing-made-simple

自分としては、テスト記述がしやすい／内容が見やすいところが気に入っている点です。


# KarateによるAPIテスト

## 利用準備

### 前提条件
- Java SE 1.8.112 以上がインストールされていること
- Maven 3.3 以上がインストールされていること

### Mavenプロジェクトの作成
最初に、Karateのプロジェクトを作成します。
手動で必要なファイルを作成しても良いのですが、今回は、Karate Maven archetype を使って空のプロジェクトを作成します。

```bash
mvn archetype:generate \
-DarchetypeGroupId=com.intuit.karate \
-DarchetypeArtifactId=karate-archetype \
-DarchetypeVersion=0.7.0 \
-DgroupId=example -DartifactId=example-karate
```

出力されたファイルの構成は、以下のようになっています。

```
example-karate/
├── pom.xml
└── src
    ├── main
    │   └── java
    └── test
        └── java
            ├── examples
            │   ├── ExamplesTest.java
            │   └── users
            │       ├── UsersRunner.java
            │       └── users.feature
            ├── karate-config.js
            └── logback-test.xml
```

|ファイル|説明|
|---|---|
|karate-config.js|Karateの設定ファイルです。|
|logback-test.xml|ログの設定ファイルです。無くても動作します。|
|ExamplesTest.java|Karateのテスト実行クラスです。|
|UsersRunner.java|シナリオごとのテスト実行クラスです。|
|users.feature|サンプルのシナリオファイルです。|


上記で注意する点として、```karate-config.js```や```.feature```のファイルが、```src/test/java```配下に出力されています。
通常のMavenプロジェクトでは、テストに関するリソースファイルは```src/test/resources```配下に置くのが一般的ですが、Karateの場合、シナリオファイルとテストクラスを同じ場所に配置することが必要になります。

このため、pom.xmlには、以下の設定がされています。

```xml
  <build>
    <testResources>
      <testResource>
        <directory>src/test/java</directory>
        <excludes>
          <exclude>**/*.java</exclude>
        </excludes>
      </testResource>
    </testResources>
  </build>
```

## テスト実施

### テストの内容

上記で作成したプロジェクトの中では、既に「users.feature」というテストシナリオのファイルが用意されています。
中身を見ると、以下のような感じになっています（一部）。

```cucumber
Feature: sample karate test script    

Background:
* url 'https://jsonplaceholder.typicode.com'

Scenario: get all users and then get the first user by id

Given path 'users'
When method get
Then status 200
```

上記では、

- **Background:**
  - "https://jsonplaceholder.typicode.com" （「JSONPlaceholder」という、REST-APIのテストが行えるサイト） というサイトに対して、
- **Scenario:**
  - "get all users and then get the first user by id" という内容のシナリオで、
- **Given-When-Then**
  - `'users'` というAPIに対して、
  - `GET` のメソッドで、 
  - `HTTPステータス 200` が返ってくることを期待する。

という内容をテストすることになります。
**「Given」「When」「Then」** が基本の構成ですね。

変数を利用したり、レスポンスの内容を確認したりといったこともできますが、どのような記述をするのかは、以下を参照してください。

- [Syntax Guide](https://github.com/intuit/karate#syntax-guide)
- [Setting and Using Variables](https://github.com/intuit/karate#setting-and-using-variables)


また、同じディレクトリに、 `UsersRunner.java` というテスト実行クラスがあります。
中身は空でも良いのですが、 `@RunWith(Karate.class)` のアノテーションが付与されている点に注意してください。

```Java
package examples.users;

import com.intuit.karate.junit4.Karate;
import org.junit.runner.RunWith;

@RunWith(Karate.class)
public class UsersRunner {

}
```

### テストの実行
Mavenから、テストを実行します。

```bash
mvn clean test
```

```bash

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running examples.ExamplesTest
14:40:33.616 [main] INFO  com.intuit.karate - karate.env system property was: null
14:40:34.123 [main] DEBUG com.intuit.karate -
1 > GET https://jsonplaceholder.typicode.com/users
1 > Accept-Encoding: gzip,deflate
1 > Connection: Keep-Alive
1 > Host: jsonplaceholder.typicode.com
1 > User-Agent: Apache-HttpClient/4.5.4 (Java/1.8.0_172)

14:40:34.465 [main] DEBUG com.intuit.karate -
1 < 200
1 < Access-Control-Allow-Credentials: true
1 < CF-Cache-Status: HIT
1 < CF-RAY: 412731e2bad9a572-NRT
1 < Cache-Control: public, max-age=14400
1 < Connection: keep-alive
1 < Content-Type: application/json; charset=utf-8

・・・

2 Scenarios (2 passed)
17 Steps (17 passed)
0m1.677s
html report: (paste into browser to view)
-----------------------------------------
file:/MyWorkspace/example-karate/target/surefire-reports/TEST-examples.users.users.html

Tests run: 19, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.02 sec

```

- 注意
  - Karate 0.7.0 以上では、Javaのバージョンは、1.8.112以上 である必要があります。1.9以上でもOKです。
  - マイナーバージョンにも注意。Javaのバージョンが不適切な場合は、以下のようなエラーが発生します。

```
java.lang.RuntimeException: evaluation of karate-config.js failed:
	at com.intuit.karate.ScriptContext.<init>(ScriptContext.java:150)
        ...
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: com.intuit.karate.exception.KarateException: javascript function call failed: 
ReferenceError: "karate" is not defined     
```

## テスト結果の確認
Karateでは、以下のようなレポートが出力され、テストの実行内容が分かります。

![karate-report.png](https://qiita-image-store.s3.amazonaws.com/0/22893/3f0d02ef-8749-f56a-7ba6-8047af44ddec.png)

# まとめ
まずは、Karateのチュートリアルを利用して、どのようにテストを実施できるのかを確認してみました。
比較的、直感的に理解できる内容になっているかと思います。

# 参考
- [Karate入門：Karateを使ったREST APIテスト](https://qiita.com/shimashima35/items/b5daaba6bd9880d91434)