はじめに
前回は、Karate Maven archetype が出力する example を実行してみました。
今回は、そのファイルの内容を見ていき、Karateに対する理解を深めていきたいと思います。
KarateのExampleのファイル内容
Karate Maven archetype で出力される内容は、以下のような構成です。
以降で、ポイントとなるファイルの内容を確認していきます。
example-karate/
├── pom.xml
└── src
├── main
│ └── java
└── test
└── java
├── examples
│ ├── ExamplesTest.java
│ └── users
│ ├── UsersRunner.java
│ └── users.feature
├── karate-config.js
└── logback-test.xml
pom.xml
前提として、今回利用しているのは Karate 0.7.0 になります。
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven.compiler.version>3.6.0</maven.compiler.version>
<karate.version>0.7.0</karate.version>
</properties>
dependencies では、以下の2つのライブラリが指定されています。
- karate-apache
- karate-junit4
2018/06/02現在、JUnit5には、まだ公式には対応されていないようです。
<dependencies>
<dependency>
<groupId>com.intuit.karate</groupId>
<artifactId>karate-apache</artifactId>
<version>${karate.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.intuit.karate</groupId>
<artifactId>karate-junit4</artifactId>
<version>${karate.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
こちらは前回の注意点の再掲ですが、buildタグでは、以下のようにsrc/test/java
をテストリソースに指定しておく必要があります(デフォルトは、scr/test/resources
)。
<build>
<testResources>
<testResource>
<directory>src/test/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
</testResources>
</build>
karate-config.js
Karate の共通設定のファイルです。
デフォルトは、以下の内容。
function() {
var env = karate.env; // get system property 'karate.env'
karate.log('karate.env system property was:', env);
if (!env) {
env = 'dev';
}
var config = {
env: env,
myVarName: 'someValue'
}
if (env == 'dev') {
// customize
// e.g. config.foo = 'bar';
} else if (env == 'e2e') {
// customize
}
return config;
}
基本的な設定内容は、以下を参照。
環境ごとに設定を切り替えたり、タイムアウト値などを設定できたりします。
httpsのREST-APIにアクセスすらう際にも、ここでSSLの設定をするらしい。
karate.configure('ssl', true);
examples/ExamplesTest.java
こちらは、テストケースを実行するためのクラス。
テストスイート(テストケースをグルーピングするもの)として振る舞います。
そのため、ディレクトリの構成は、後述する XxxRunner
との関係性を意識する必要があります。
package examples;
import com.intuit.karate.junit4.Karate;
import org.junit.runner.RunWith;
@RunWith(Karate.class)
public class ExamplesTest {
// this will run all *.feature files that exist in sub-directories
// refer to https://github.com/intuit/karate#naming-conventions
// for folder-structure recommendations and naming conventions
}
examples/users/UsersRunner.java, users.feature
こちらが、テストケースの本体の内容です。
Runnerのファイルと、featureのファイルがセットになりますが、1:1である必要はありません。
Runnerのテスト実行クラスがあるディレクトリのfeatureファイルが読み込まれ、テストシナリオとして実行されます。
package examples.users;
import com.intuit.karate.junit4.Karate;
import org.junit.runner.RunWith;
@RunWith(Karate.class)
public class UsersRunner {
}
Feature: sample karate test script
If you are using Eclipse, install the free Cucumber-Eclipse plugin from
https://cucumber.io/cucumber-eclipse/
Then you will see syntax-coloring for this file. But best of all,
you will be able to right-click within this file and [Run As -> Cucumber Feature].
If you see warnings like "does not have a matching glue code",
go to the Eclipse preferences, find the 'Cucumber User Settings'
and enter the following Root Package Name: com.intuit.karate
Refer to the Cucumber-Eclipse wiki for more: http://bit.ly/2mDaXeV
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
* def first = response[0]
Given path 'users', first.id
When method get
Then status 200
Scenario: create a user and then get it by id
* def user =
"""
{
"name": "Test User",
"username": "testuser",
"email": "test@user.com",
"address": {
"street": "Has No Name",
"suite": "Apt. 123",
"city": "Electri",
"zipcode": "54321-6789"
}
}
"""
Given url 'https://jsonplaceholder.typicode.com/users'
And request user
When method post
Then status 201
* def id = response.id
* print 'created id is: ' + id
Given path id
# When method get
# Then status 200
# And match response contains user
テストスクリプト(featureファイル)の書き方
テストスクリプトの書き方については、主に以下の内容を参照してください。
基本構造
基本的な構造は、以下のようになります。
Feature: テストの概要
テストの詳細内容の記述。
複数行で記述可能。
Background:
# (optional)
# 全シナリオで共通的に利用するグローバル変数などを定義する。
# 各シナリオの前に実行される。
Scenario: シナリオの概要
# テストケースの記述
#
# Given-When-Then-And -> テストの振る舞いの記述
# * def -> 変数の定義
# * print -> コンソールへの出力
Scenario: シナリオの概要
# テストケースの記述
テストシナリオ
Given-When-Then-And
テストシナリオの基本構文となるテストステップの記述です。
ひとつの Scenario:
の中に、複数記述することができます。
- Given
- オブジェクトの作成や設定など、前提条件を記述する。
- When
- イベントやアクションを記述する。REST-APIの呼び出しなどを行う部分。
- Then
- 期待される結果を記述する。アサーションを使用し、実際の結果と記述した期待結果を比較する。
- And
- 直前のステップと同じ意味をもつ。複数の定義や条件を連続して記述する際に利用する。
以下のシナリオの内容を見てみます。
このシナリオでは、 JSONPlaceholder のAPIを利用しています。
Scenario: get all users and then get the first user by id
Given path 'users'
When method get
Then status 200
* def first = response[0]
Given path 'users', first.id
When method get
Then status 200
Backgroundで、* url 'https://jsonplaceholder.typicode.com'
と記述されているため、最初のテストステップでは、以下の内容を実行していることになります。
(1)Given path 'users'
(2)When method get
(3)Then status 200
- (1)
https://jsonplaceholder.typicode.com/users
のAPI(userの一覧取得)をテストする。 - (2)
HTTP GET
でアクセスする。 - (3)
HTTPステータス200
であることを確認する。
次のテストステップでは、前ステップのレスポンスを利用して、以下の内容を実行しています。
(1)* def first = response[0]
(2)Given path 'users', first.id
(3)When method get
(4)Then status 200
- (1)一覧取得のレスポンスから、最初の1件を取得する。
- (2)
https://jsonplaceholder.typicode.com/users/{id}
のAPIをテストする。- このとき、(1)で取得したデータの
id
をパラメータとして指定する。
- このとき、(1)で取得したデータの
- (3)
HTTP GET
でアクセスする。 - (4)
HTTPステータス200
であることを確認する。
このようにして、テストシナリオを記述することができます。
また、以下のように、変数を指定してAPIの実行をすることも可能です。
* def user =
"""
{
"name": "Test User",
"username": "testuser",
"email": "test@user.com",
"address": {
"street": "Has No Name",
"suite": "Apt. 123",
"city": "Electri",
"zipcode": "54321-6789"
}
}
"""
Given url 'https://jsonplaceholder.typicode.com/users'
And request user
When method post
Then status 201
拡張
example の内容にはありませんが、テストスクリプトのファイル内では、JavaScriptで関数を定義したり、さらにその中でJavaのクラスを呼び出したりすることができます。
これにより、多少複雑な処理が伴ったり、対象アプリの処理を実行したりする場合でも、テストシナリオを完結に記述することが可能になります。
例えば、現在日時を基準に、動的に日時を示す文字列を取得したい場合、以下のような記述で可能です。
* def getTime =
"""
function(min) {
var dateFormatType = Java.type('java.text.SimpleDateFormat');
var dateFormat = new dateFormatType("yyyy-MM-dd'T'HH:mm:ss.SSS+09:00");
var date = new java.util.Date();
date.setTime(date.getTime() + min*60*1000);
return dateFormat.format(date);
}
"""
* def timestamp = {"timestamp": #(getTime(0))}
* print timestamp
これらの機能を使うことで、テストケースを効率よく作成していくことができそうですね!
詳細は、こちらを参照。
まとめ
example の内容を元に、Karateのテストの内容がどのような構成・記述になっているのかを確認しました。
テストシナリオの内容も、直感的に理解できる内容だと感じます。
Karateの機能は、ここに書いただけでなく、README の内容を見ると、他にもいろいろとあるようです。
その辺りは、別途、確認していきたいと思っています。