Contents
Karateとは
KarateはWeb API用のテストフレームワークである。
Gherkin(ガーキン)形式(featureファイル)でテストケースを記述できるので、ビルド不要であり、また、同じAPIに対して複数のテストケースを作成する場合に便利である。
JVM上で動作する。必要に応じてテストケース中にJavaコードやJavaScriptコードを埋め込むことが可能。
負荷試験ツールであるGatlingと連携して負荷試験も可能である。
テスト結果のレポートはHTMLとして出力可能。
公式サイト:https://github.com/intuit/karate
紹介資料
- https://www.slideshare.net/takanorig/api-with-karate-146047857
- (動画)https://www.youtube.com/watch?v=YWK4J3lhFw4
- (動画)https://www.youtube.com/watch?v=LJJmSXJJTaY
仕様説明書
https://intuit.github.io/karate/
環境構築
Javaのインストール
JREの1.8.0_112以上が必要である。※ただし、最新版のJavaではJRE単体では配布されず、JDKにバンドルされるので、JDKをインストールする
Apacheのインストール
テスト実行結果はターミナルにも出力されるが、HTMLとしてレポートにも出力される。このHTMLはテスト実行のたびに上書きされてしまうが、HTMLのほうが見やすいので、Webサーバーを用意してWebブラウザでみられるようにしたほうが良い。
Webサーバーは何でもよいが、今回はApacheとした。
Karateのインストール
KarateはMavenやGradleを使用したデプロイ、またはjarファイルの配置によるインストールが可能である。
Mavenによるビルド
- ソースコードをダウンロードする
curl -L -s https://github.com/intuit/karate/archive/master.zip -o karate-master.zip
- 展開する
unzip karate-master.zip
cd karate-master - ビルドする
mvn compile
- ビルドしたファイルを移動する
jarファイル配置によるインストール
この方法では、gatlingによる負荷試験が行えないが、gatlingは別途インストールできる。
- 最新版のビルドファイルのURLを確認する
https://dl.bintray.com/ptrthomas/karate/
※URL中のパスにコロン(:)が入っている場合、除外すること - jarファイルをダウンロードする
curl -s -L -O https://dl.bintray.com/ptrthomas/karate/karate-0.9.4.jar
- インストール場所に移動する
mkdir /usr/local/karate
mv karate-* /usr/local/karate/ - リンクを作成する
ln -s /usr/local/karate/karate-0.9.4.jar /usr/local/karate/karate.jar
- エイリアスを設定する
alias karate='java -jar /usr/local/karate/karate.jar'
- エイリアスは永続化しておく
echo "" >> /etc/bashrc echo "# KARATE ENVIRONMENT VARIABLE" >> /etc/bashrc echo "alias karate='java -jar /usr/local/karate/karate.jar'" >> /etc/bashrc
- 実行できることを確認する
karate -h
実行例07:53:17.835 [main] INFO com.intuit.karate.Main - Karate version: 0.9.4
Usage: <main class> [-hsu] [-c=<cert>] [-e=<env>] [-k=<key>] [-m=<mock>]
[-n=<name>] [-o=<output>] [-p=<port>] [-T=<threads>]
[-t=<tags>]... [<tests>...]
[<tests>...] one or more tests (features) or search-paths to run
-c, --cert=<cert> ssl certificate (default: cert.pem)
-e, --env=<env> value of 'karate.env'
-h, --help display this help message
-k, --key=<key> ssl private key (default: key.pem)
-m, --mock=<mock> mock server file
-n, --name=<name> scenario name
-o, --output=<output> directory where logs and reports are output (default
'target')
-p, --port=<port> mock server port (required for --mock)
-s, --ssl use ssl / https, will use 'cert.pem' and 'key.pem' if
they exist in the working directory, or generate them
-t, --tags=<tags> cucumber tags - e.g. '@smoke,~@ignore'
-T, --threads=<threads> number of threads when running tests
-u, --ui show user interface
Hello World
簡単な動作の確認を行う
モックの用意
モックはデモ用のものを使用する
- 任意のパスにfeatureファイルを作成する
vi hello-world-mock.feature
ファイルの内容を張り付けて保存する
Feature: test hello world
Scenario: pathMatches('/hello')
* def message = 'Hello World'
* def response = { message: '#(message)' } - モックサーバーを起動する
karate -m hello-world.feature -p 8080
※カレントディレクトリにtargetディレクトリが作成され、その中にログファイルが生成されるので注意
テストケースの用意
モック起動中はターミナルがブロックされるので、新しいターミナルを開く。
- 任意のパスにfeatureファイルを作成する
vi hello-world-test.feature
ファイルの内容を張り付けて保存する。成功するシナリオ、失敗するシナリオを定義している。Feature: test hello world
Scenario: test /hello
When url 'http://localhost:8080/hello'
And method get
Then status 200
And def message = 'Hello World'
And match response == { message: '#(message)' }
Scenario: test /hello fail
When url 'http://localhost:8080/hello'
And method get
Then status 200
And def message = 'Hello World XXX'
And match response == { message: '#(message)' } - テストを実行する
karate hello-world-test.feature
実行例
シナリオのうち、1つが成功、1つが失敗している。
[root@server path]# java -jar /usr/local/karate/karate.jar hello-world-test.feature
06:08:23.907 [main] INFO com.intuit.karate.Main - Karate version: 0.9.4
Warning: Nashorn engine is planned to be removed from a future JDK release
06:08:26.037 [ForkJoinPool-1-worker-3] DEBUG com.intuit.karate - request:
1 > GET http://localhost:8080/hello
1 > Accept-Encoding: gzip,deflate
1 > Connection: Keep-Alive
1 > Host: localhost:8080
1 > User-Agent: Apache-HttpClient/4.5.5 (Java/12.0.2)
06:08:26.073 [ForkJoinPool-1-worker-3] DEBUG com.intuit.karate - response time in milliseconds: 32.60
1 < 200
1 < Content-Type: application/json
{"message":"Hello World"}
06:08:26.124 [ForkJoinPool-1-worker-3] DEBUG com.intuit.karate - request:
1 > GET http://localhost:8080/hello
1 > Accept-Encoding: gzip,deflate
1 > Connection: Keep-Alive
1 > Host: localhost:8080
1 > User-Agent: Apache-HttpClient/4.5.5 (Java/12.0.2)
06:08:26.139 [ForkJoinPool-1-worker-3] DEBUG com.intuit.karate - response time in milliseconds: 13.71
1 < 200
1 < Content-Type: application/json
{"message":"Hello World"}
06:08:26.145 [ForkJoinPool-1-worker-3] ERROR com.intuit.karate - assertion failed: path: $.message, actual: 'Hello World', expected: 'Hello World XXX', reason: not equal
06:08:26.317 [pool-1-thread-1] INFO com.intuit.karate.Runner - <<fail>> feature 1 of 1: hello-world-test.feature
---------------------------------------------------------
feature: hello-world-test.feature
report: target/surefire-reports/hello-world-test.json
scenarios: 2 | passed: 1 | failed: 1 | time: 0.6579
---------------------------------------------------------
Karate version: 0.9.4
======================================================
elapsed: 2.29 | threads: 1 | thread time: 0.66
features: 1 | ignored: 0 | efficiency: 0.29
scenarios: 2 | passed: 1 | failed: 1
======================================================
failed features:
hello-world-test: hello-world-test.feature:15 - path: $.message, actual: 'Hello World', expected: 'Hello World XXX', reason: not equal
Exception in thread "main" picocli.CommandLine$ExecutionException: there are test failures
at com.intuit.karate.Main$1.handleExecutionException(Main.java:133)
at picocli.CommandLine.parseWithHandlers(CommandLine.java:1157)
at com.intuit.karate.Main.main(Main.java:139)
テストケース文法
テストケースはGherkin(ガーキン)形式
参考:https://github.com/intuit/karate#syntax-guide
テストケースの構成
テストケースはセクションを定義し、その中にシナリオとして各テストケースを、その中にステップとして各処理内容を定義する構成である。
Feature: brief description of what is being tested more lines of description if needed. Background: # this section is optional ! # steps here are executed before each Scenario in this file # variables defined here will be 'global' to all scenarios # and will be re-initialized before every scenario Scenario: brief description of this scenario # steps for this scenario Scenario: a different scenario # steps for this other scenario
セクション
テストケースはいくつかのセクションに分かれる。
セクション中に具体的なテスト内容(ステップ)を定義することになる。
セクションの書式は次の通り
<セクション名>: <セクション内設定>
<セクション内設定>
...
なお、一度のテスト実行ですべてのセクションが実行されるので、テストを分けたい場合は別のfeatureファイルに記述する。
Feature
このファイルの説明文を記述する。テスト内容に影響を与えないコメントである。
このセクションはファイルの冒頭に1つ定義する。
- 書式
Feature: <説明文> <説明文>
...
Background
各シナリオセクションの実行前に共通して実行する内容を定義する。
このセクションはオプションであり、定義を省略可能である。
- 書式
Background: <ステップ>
...
Scenario
個別のテスト内容を定義する。
各シナリオはシナリオ名で特定される。
- 書式
Scenario: <シナリオ名> <ステップ>
...
ステップ
ステップはテスト内の1命令を表す。
ステップの冒頭に以下のステップ接頭辞のいずれかを記述する。
- Given
- And
- When
- Then
- *
各ステップ接頭辞に違いは無く、任意の使い分けをするとよい。使い分けをしないのであれば、アスタリスクに統一すればよい。
一般的な使い分けは以下である。
- Given
テストを行うための前提条件。つまりテスト実行の際の初期化を行う。 - When
テストの振る舞い。つまりテスト対象に対する操作を記述する。 - Then
テスト結果の期待値。 - And
その他のステップについて複数記載したい場合に使用する。
参考:https://martinfowler.com/bliki/GivenWhenThen.html
- 書式
<ステップ接頭辞> <ステップ式> ...
ステップ式
全般
def
変数に値を設定する。定義した変数はほかの式で使用可能になる。
https://github.com/intuit/karate#def
- 例
* def myNum = 5
変数を参照する際は次のように記述する。
- 文字列中、JSON中
'#(<変数名>)'
例:’VAR_#(var1)_VAR’ - そのまま参照
<変数名>
例:’VAR_’+var1+’_VAR’
変数を文字列と連結したり、substringメソッドで変形したりする場合、’#(<変数名>)’の形態ではできない。従って、JSONに変形した文字列を格納したい場合は一旦別の変数に格納しなおす必要がある。
例
* def a = 'a'
* def b = a+'b'
* def j1 = {val:#(ab)}
# 誤り
* def j2 = {val:a+'b'}
* def j3 = {val:#(a)+'b'}
ログに任意の文字列を出力する
https://github.com/intuit/karate#print
- 例
* print 'the value of a is:', a
replace
文字列の置換を行う
https://github.com/intuit/karate#replace
- 例
* def text = 'hello <foo> world' * replace text.foo = 'bar' * match text == 'hello bar world'
yaml
yaml形式のテキストをJSONオブジェクトに変換する。
yamlの状態ではオブジェクトではなく、あくまでテキストとして扱われる。
https://github.com/intuit/karate#yaml
csv
csv形式のテキストをJSONオブジェクトに変換する。
https://github.com/intuit/karate#csv
read
ファイルを読み込む
https://github.com/intuit/karate#reading-files
- 例
* def someJson = read('some-json.json') * def moreJson = read('classpath:more-json.json')
型変換
https://github.com/intuit/karate#type-conversion
configure
テストごとの個別の環境設定を行う。
https://github.com/intuit/karate#configure
call
JavaScriptの関数を呼び出す。他のfeatureファイルで定義した関数も呼び出せる。
https://github.com/intuit/karate#call
テストの振る舞い
url
テスト対象のURLを設定する
https://github.com/intuit/karate#url
- 例
Given url 'https://' + e2eHostName + '/v1/api'
path
テスト対象のパスを設定する。パスはurl式でも設定可能である。
複数回呼び出すと、最後に文字列が連結される。
https://github.com/intuit/karate#path
- 例
Given path 'documents/' + documentId + '/download' # this is equivalent to the above Given path 'documents', documentId, 'download' # or you can do the same on multiple lines if you wish Given path 'documents' And path documentId And path 'download'
request
POSTする際に送信するリクエストを記述する。
https://github.com/intuit/karate#request
- 例
Given request { name: 'Billie', type: 'LOL' }
method
HTTPメソッドを指定する。
使用できるメソッドは次の通り。
- get
- post
- put
- delete
- patch
- options
- head
- connect
- trace
https://github.com/intuit/karate#method
- 例
# set headers or params (if any) BEFORE the method step Given header Accept = 'application/json' When method get # the step that immediately follows the above would typically be: Then status 200
status
テスト結果のステータスコードをチェックする。
https://github.com/intuit/karate#status
- 例
Then status 200
param / params
クエリストリングのパラメータを指定する。
https://github.com/intuit/karate#param
https://github.com/intuit/karate#params
- 例
Given param someKey = 'hello' And param anotherKey = someVariable
header / headers
HTTPヘッダの値を設定する。
https://github.com/intuit/karate#header
https://github.com/intuit/karate#headers
- 例
Given header Authorization = myAuthFunction() And header transaction-id = 'test-' + myIdString
送出するCookieを指定する。
https://github.com/intuit/karate#cookie
https://github.com/intuit/karate#cookies
- 例
Given cookie foo = 'bar'
form field
HTMLフォームからデータを送信する際に行われるURLエンコードを行ってデータ送信する。
https://github.com/intuit/karate#form-field
- 例
Given path 'login' And form field username = 'john' And form field password = 'secret' When method post Then status 200 And def authToken = response.token
multipart file
送信するファイルを指定する
https://github.com/intuit/karate#multipart-file
- 例
Given multipart file myFile = { read: 'test.pdf', filename: 'upload-name.pdf', contentType: 'application/pdf' } And multipart field message = 'hello world' When method post Then status 200
retry until
レスポンスが毎回変わりうる際に、特定の条件を満たすまでHTTPリクエストを繰り返す。
最大のリトライ回数はパラメータretryで設定可能。
https://github.com/intuit/karate#retry-until
- 例
Given url demoBaseUrl And path 'greeting' And retry until response.id > 3 When method get Then status 200
テスト結果の確認
assert
特定の条件を満たすかを検査する。
検査式にはTrueになる条件を記載する。
単純な値の比較のみしかできず、JSONのような構造体の一括の比較の場合はmatch式を使用する。
https://github.com/intuit/karate#assert
- 例
Given def color = 'red ' And def num = 5 Then assert color + num == 'red 5'
match
JSON、XMLの構造体の内部の値を一括して比較する。
https://github.com/intuit/karate#match
- 例
* def myJson = { foo: 'bar' } * set myJson.foo = 'world' * match myJson == { foo: 'world' }
比較演算子
比較演算子は==以外にも使用できるものがある。
- contains
指定の値はチェックするが、指定していない値が存在する場合はチェックせず、パスする - !contains
指定の値が含まれないことをチェックする
マーカー
マーカーとして値のパターンとして比較できる。
例えば次のように指定することで、値の内容にかかわらず、文字列型かどうかをチェックされる。
* match myJson == { foo: '#string
' }
使用可能なマーカー
Marker | Description |
---|---|
#ignore |
Skip comparison for this field even if the data element or JSON key is present |
#null |
Expects actual value to be null , and the data element or JSON key must be present |
#notnull |
Expects actual value to be not-null |
#present |
Actual value can be any type or even null , but the key must be present (only for JSON / XML, see below) |
#notpresent |
Expects the key to be not present at all (only for JSON / XML, see below) |
#array |
Expects actual value to be a JSON array |
#object |
Expects actual value to be a JSON object |
#boolean |
Expects actual value to be a boolean true or false |
#number |
Expects actual value to be a number |
#string |
Expects actual value to be a string |
#uuid |
Expects actual (string) value to conform to the UUID format |
#regex STR |
Expects actual (string) value to match the regular-expression ‘STR’ (see examples above) |
#? EXPR |
Expects the JavaScript expression ‘EXPR’ to evaluate to true, see self-validation expressions below |
#[NUM] EXPR |
Advanced array validation, see schema validation |
#(EXPR) |
For completeness, embedded expressions belong in this list as well |
Or一致
JavaScriptマーカーを使用すれば値のOrマッチができる。
例:
* def val1 = 1
* def val2 = 2
* def a =
"""
{
val : '#(val1)',
}
"""
* def b =
"""
{
val : '#? _ == val1 || _ == val2',
}
"""
* match a == b
set
JSON内部の特定の値を変更する
https://github.com/intuit/karate#set
値
値はJavaScriptと同様の記載方法で定義できる。
つまり、数値はそのまま、文字列はダブルクォーテーションかシングルクォーテーションで囲う。
構造体
JSON形式やXML形式などの構造をなす値もそのまま値として記述可能である。
- JSON例
* def cats = [{ name: 'Billie' }, { name: 'Bob' }] * match cats[1] == { name: 'Bob' }
- XML例
Given def user = { name: 'john', age: 21 }
文字列の連結
構造体のみ値内で文字列の連結が可能である。
- 例
この例では文字列“hello “と変数nameを連結している。
* def foo = { bar: '#("hello " + name)' }
ヒアドキュメント
値が複数行にわたって記述したい場合、次のようにして記述することができる。
- JSON例
* def cat = """ <cat> <name>Billie</name> <scores> <score>2</score> <score>5</score> </scores> </cat> """
- XML例
Given request """ <?xml version='1.0' encoding='UTF-8'?> <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"> <S:Body> <ns2:QueryUsageBalance xmlns:ns2="http://www.mycompany.com/usage/V1"> <ns2:UsageBalance> <ns2:LicenseId>12341234</ns2:LicenseId> </ns2:UsageBalance> </ns2:QueryUsageBalance> </S:Body> </S:Envelope> """
構造体の無視
JSON形式やXML形式で記述すると自動的にその構造体として扱われるが、構造体としてではなく、テキストとして扱いたい場合は特別な対応が必要である。
text式をdef式の代わりに使用する。
- 例
* text query = """ { hero(name: "<name>") { height mass } } """
表
JSON形式で同じフィールドを持つオブジェクトが複数続く場合、表形式として定義可能である。
https://github.com/intuit/karate#table
- 例
* table cats | name | age | | 'Bob' | 2 | | 'Wild' | 4 | | 'Nyan' | 3 | * match cats == [{name: 'Bob', age: 2}, {name: 'Wild', age: 4}, {name: 'Nyan', age: 3}]
特別な値
https://github.com/intuit/karate#special-variables
response
HTTP実行結果のレスポンスが格納される
https://github.com/intuit/karate#response
responseCookies
HTTP実行で返されたCookieが格納される
https://github.com/intuit/karate#responsecookies
responseHeaders
HTTP実行で返されたヘッダーが格納される
https://github.com/intuit/karate#responseheaders
responseStatus
HTTP実行で返されたステータスコードが格納される
https://github.com/intuit/karate#responsestatus
karate
Karate環境を操作するオブジェクトが格納される
https://github.com/intuit/karate#the-karate-object
その他
コメント
#で始まる行はコメントとして扱われる。
行の途中からコメントにはできない。
三項演算
三項演算を使用できる。
- 例
* def putOrPost = (someVariable == 'dev' ? 'put' : 'post')
環境設定ファイル
Karateでは環境設定ファイルを用意し、テスト環境に対しての設定を行うことができる。
設定では、各テストケースで共通に使用できるパラメータや、Karate環境に対する設定を行うことができる。
設定ファイルはファイル名”karate-config.js”として作成し、内部にはJavaScript形式でJSONオブジェクトを返す関数を1つ定義しなければならない。
その返り値のJSON中でパラメータを設定する。
karate-config.jsファイルはclasspathに配置する必要がる。カレントディレクトリをクラスパスに追加する設定をしているなら、karateの実行ディレクトリに配置しておけばよい。
参考:https://github.com/intuit/karate#configuration
設定サンプル
https://github.com/intuit/karate#karate-configjs
環境設定
設定ファイルで定義する関数ではkarate変数が暗黙の変数として使用できる。
karateオブジェクトではいくつかのメソッドを持つが、configureメソッドではKarate環境に対する設定を行える。
なお、テストケース内でも個別に設定を上書きすることが可能である。
参考:https://github.com/intuit/karate#configure
- 設定ファイル内での書式
karate.configure('<パラメータ>', <値>);
- テストケース中での書式
* configure <パラメータ> = <値>
設定したパラメータは変数として参照できる
負荷試験(Gatling連携)
参考:https://github.com/intuit/karate/tree/master/karate-gatling
インストール
GatlingはMavenなどで自身でソースコードからビルドした場合はjarファイルに含まれているが、ビルド済みのjarファイルをダウンロードしてKarateをインストールした場合は含まれていない。
この場合は別途インストールする。なお、インストールはデモ用のリポジトリを使用するのが簡単である。
参考:https://github.com/ptrthomas/karate-gatling-demo
- Mavenが必要なので、事前にインストールしておくこと。
- インストールするディレクトリを作成する
mkdir /usr/local/gatling
cd /usr/local/gatling - 簡単にアクセスできるように環境変数を設定する
export GATLING_HOME=/usr/local/gatling
- 環境変数を再起動後も有効にする
echo "" >> /etc/bashrc echo "# GATLING ENVIRONMENT VARIABLE" >> /etc/bashrc echo "export GATLING_HOME=$GATLING_HOME" >> /etc/bashrc
- ソースコードなどをダウンロードする
curl -s https://codeload.github.com/ptrthomas/karate-gatling-demo/zip/master -o karate-gatling-demo-master.zip
- アーカイブを展開する
unzip karate-gatling-demo-master.zip
rm -f karate-gatling-demo-master.zip - プロジェクトをコピーして使うためのテンプレートを作成する
- デモプロジェクトをコピーする
cp -r karate-gatling-demo-master template
- 不要なファイルを削除する
rm -f template/README.md
rm -f template/src/test/java/mock/*feature
rm -f template/src/test/java/mock/CatsGatlingSimulation.scala
rm -f template/.gitignore - テスト実行用ファイルを標準的な名前に変更する
mv template/src/test/java/mock/CatsKarateSimulation.scala template/src/test/java/mock/StartGatlingWithKarate.scala
sed "s/CatsKarateSimulation/StartGatlingWithKarate/" -i template/src/test/java/mock/StartGatlingWithKarate.scala
sed "s/CatsKarateSimulation/StartGatlingWithKarate/" -i template/pom.xml - 実行レポートの出力場所を共通化する。
レポートはHTMLで出力されるため、共通化してそのパスをWebサーバーで公開するのが良い。mkdir report
mkdir template/target/
cd template/target/
ln -s /usr/local/gatling/report gatling - Apacheで公開設定を行う
vi /etc/httpd/conf/httpd.conf
以下のように公開する
Alias /gatling "/usr/local/gatling/report"
<Directory "/usr/local/gatling/report"> AuthType Digest
AuthName html
AuthUserFile conf/auth
AuthGroupFile conf/auth
Require valid-user
Options Indexes
AllowOverride None
</Directory> - Apacheの設定を反映する
systemctl restart httpd
- デモプロジェクトをコピーする
- デモ用のテストを実行する場合は、次を行う。実施しなくてもよいが、実施すると、問題なくビルドできているかを確認できる。
- デモ用ディレクトリに移動する
cd karate-gatling-demo-master/
- ビルドする。
※テスト用のコードとして配置されているので、フェーズ”test-compile”を使用する。mvn test-compile
- テストを実行する
mvn gatling:test
- デモ用ディレクトリに移動する
デモプロジェクトについて
デモプロジェクトのテストコードを改修することで、自身のテストをgatlingに移行しやすい。
また、モック付きのテスト用のコードがgatling自体にも添付されている。
https://github.com/intuit/karate/tree/master/karate-gatling/src/test
これを改修したデモプラグラムは上記の改修版なので、両者を比較すれば、自分のテストコードで負荷試験を行う際の改修箇所・方法について理解しやすい。
https://github.com/ptrthomas/karate-gatling-demo/tree/master/src/test/java
デモプロジェクトの構成
ダウンロードしたkarate-gatling-demo-masterには以下のファイルが含まれる
- pom.xml
- README.md
- src/test/java/karate-config.js
- src/test/java/logback-test.xml
logbackというロギングライブラリで使用する設定ファイル。ファイル名は「logback-test.xml」で固定である。
参考:https://logback.qos.ch/manual/configuration_ja.html - src/test/java/mock/cats-create.feature
デモ用テストファイル。”CatsKarateSimulation.scala”から呼び出される。 - src/test/java/mock/cats-delete.feature
デモ用テストファイル。”CatsKarateSimulation.scala”から呼び出される。 - src/test/java/mock/cats-delete-one.feature
デモ用テストファイル。”CatsKarateSimulation.scala”から間接的に呼び出される。 - src/test/java/mock/CatsGatlingSimulation.scala
CatsKarateSimulation.scalaのテストをKarateを使用せずに行うバージョン。Karateを使用する限り特にこのファイルは不要である。 - src/test/java/mock/CatsKarateSimulation.scala
Karateのfeatureファイルを使用してgatlingによるテストを実行する本体ファイル。MockUtilsの呼び出しによる、モックサーバーの起動も行う。 - src/test/java/mock/custom-rpc.feature
デモ用テストファイル。”CatsKarateSimulation.scala”から呼び出される。 - src/test/java/mock/feeder.feature
デモ用モックテストファイル。”MockUtils.java”から呼び出される。モックで使用するパラメーターとして使用される。 - src/test/java/mock/mock.feature
デモ用モックテストファイル。”MockUtils.java”から呼び出される。 - src/test/java/mock/MockUtils.java
モック用サーバーを起動する。 - target配下
ビルド後に生成されるファイル群。特に気にしなくてよい。
自身で改修する際に使用するには以下である。
- src/test/java/mock/CatsKarateSimulation.scala
呼び出すテスト用featureファイルや呼び出し時の各種パラメータを指定する。ファイル名は任意に変更してよい。
MockUtilsの呼び出しもこのファイルで行うので、モックが不要であれば、削除すればよい。 - src/test/java/mock/XXX.feature
上記scalaファイルから呼び出されるテスト定義ファイル。必要な分だけ用意する。基本的にKarateで使用していたものをそのまま使用可能。 - src/test/java/mock/MockUtils.java
モックが必要な場合に使用する。単にmock.featureを呼び出すだけでも良いし、必要であればパラメータを設定してもよい。 - src/test/java/mock/mock.feature
MockUtils.javaから呼び出されるモック用テスト定義ファイル。 - src/test/java/karate-config.js
Karateの設定ファイル。Karateで使用していたものをそのまま流用するのでよい。
テストコード改修
作成したテンプレートプロジェクトを元にすれば簡単に各テストシナリオ毎のgatlingプロジェクトを作成できる。
- テンプレートをコピーする
cd $GATLING_HOME
cp -r template <任意のプロジェクト名>
cd <任意のプロジェクト名> - モックが不要であれば、モックプログラムは削除する
rm -f src/test/java/mock/MockUtils.java
- 使用したいkarate-config.jsがあれば、コピーする。なければスキップ。
cp <任意のパス> src/test/java/karate-config.js
- 用意したfeatureファイルをコピーする
cp <任意のパス> src/test/java/mock/
- テスト実行内容を記述する
詳細は以下「テスト実行ファイルの記述方法」を参考vi src/test/java/mock/StartGatlingWithKarate.scala
- テストを実行する
mvn test-compile gatling:test
※テスト再実行の場合mvn gatling:test
テスト実行ファイルの記述方法
karateProtocolの生成
karateProtocolはkarateによりテスト実行する対象パスを宣言する。これにより、宣言したパスの各HTTPメソッド単位でレポートが出力される。パスは”{}”で囲うことで、集約できる。
pauseForにより、各HTTPリクエストごとの、待機時間(ミリ秒)を指定できる。待機時間を設定しない(Nilにする)場合は待機せずにすぐに次のリクエストを送信する。
なお、パスの指定は完全一致で行う。
下記の場合、pauseForではパス”/cats“はgetメソッドは15ミリ秒待機し、postメソッドは25ミリ秒待機する。
レポートは“/cats/{id}“のget、post、delete、putと”/cats”のget、post、delete、putのそれぞれで集計される。
val protocol = karateProtocol( "/cats/{id}" -> Nil, "/cats" -> pauseFor("get" -> 15, "post" -> 25) )
nameResolverの設定
nameResolverは出力レポートをさらに分離したい際に使用する。nameResolverの設定は任意である。
protocol.nameResolver = (req, ctx) => req.getHeader("karate-name")
シナリオの生成
使用するシナリオ(featureファイル)を指定する。
scenarioメソッドの引数の文字列は任意の文字列で、特に意味はない。
参考:https://gatling.io/docs/2.3/general/scenario/
下記の「@name=delete」の記述は、featureファイルの複数のシナリオのうち、実行するものを指定している。
val create = scenario("create").exec(karateFeature("classpath:mock/cats-create.feature")) val delete = scenario("delete").exec(karateFeature("classpath:mock/cats-delete.feature@name=delete"))
cats-delete.featureファイルの抜粋
~~~
Scenario: this scenario will be ignored because the gatling script looks for the tag @name=delete
* print 'this should not appear in the logs !'
When method get
Then status 400
@name=delete
Scenario: get all cats and then delete each by id
~~~
テストの実行
定義した方法でテストを実行する。実行方法はinjectメソッドで指定し、複数方法でテストする場合は、コンマ区切りで複数記述する。
テスト方法は以下が指定できる。なお、時間は単位を省略可能で、その場合は単位は秒となる。分の場合はminutesを指定する。明示的に秒を指定する場合はsecondsを指定する。
現在のバージョンではいくつか使用できないものがあった。
参考:https://gatling.io/docs/2.3/general/simulation_setup#injection
参考:https://mussyu1204.myhome.cx/wordpress/it/?p=397
- nothingFor(<時間>)
指定した時間だけ一時停止する - atOnceUsers(<リクエスト数>)
一度に指定した数のリクエストを実行する
※使用可能確認済み - rampUsers(<リクエスト数>) during (<時間>)
指定した時間をかけてリクエストを線形に増やしながら、指定した数のリクエストを実行する
※使用可能確認済み - heavisideUsers(<リクエスト数>) over (<時間>)
指定した時間をかけて、指定した時間の中ほどにピークが来るように山形にリクエストを増やしながら、指定した数のリクエストを実行する - constantUsersPerSec(<リクエスト数>) during (<時間>)
指定した時間の間、毎秒指定した数の新しいリクエストが行われる
※使用可能確認済み - constantUsersPerSec(<リクエスト数>) during (<時間>) randomized
指定した時間の間、ランダム秒ごとに指定した数の新しいリクエストが行われる - rampUsersPerSec(<初期リクエスト数>) to <最終リクエスト数> during(<時間>)
指定した時間をかけて指定した初期リクエストから最終リクエスト数まで、毎秒リクエストを増やす - rampUsersPerSec(<リクエスト数>) to <リクエスト数> during (<時間>) randomized
指定した時間をかけて指定した初期リクエストから最終リクエスト数まで、ランダム秒ごとにリクエストを増やす - splitUsers(<最終リクエスト数>) into(rampUsers(<追加リクエスト数>) over(<実行時間>)) separatedBy(<時間間隔>)
指定した実行時間までに、指定した時間間隔で指定した追加リクエスト数を増やしていき、最終的に指定した最終リクエスト数まで増加させる。 - splitUsers(<最終リクエスト数>) into(rampUsers(<追加リクエスト数>) over(<時間>)) separatedBy atOnceUsers(<追加リクエスト数>)
指定した実行時間までに、指定した時間間隔で指定した追加リクエスト数を増やしていく。次に、一度に指定した追加リクエスト数を増やす。これを交互に繰り返し、最終的に指定した最終リクエスト数まで増加させる。
例:
setUp( create.inject(rampUsers(10) during (5 seconds)).protocols(protocol), delete.inject(rampUsers(5) during (5 seconds)).protocols(protocol) )