Help us understand the problem. What is going on with this article?

Gatling の exec メソッド内の記述方法と、Sessionの扱い方のまとめ

More than 3 years have passed since last update.

概要

Gatlingのテストコードを記述するためにはexec内の書き方とSessionの扱い方を覚えなければいけません。
どれも http://gatling.io/docs/2.1.7/index.html に記述されていることですが、
日本語で記載された記事が少ないので、全てではありませんが簡単にまとめました。
チュートリアルを実行できたレベルの人が見ることを想定してまとめています。

※この記事は、「Gatlingで何が出来るか」ではなく、「Gatlingのメソッドの使い方やコードの書き方」の視点より書いています。

複数のexec処理を実行する

execをメソッドチェーンで記述する

execが複数ある時の一番シンプルな書き方です。Gatlingのサンプルにも記述されてます。

val search_exec = 
  exec(http("Home")
  .exec(http("Search").get("/computers?f=macbook"))
  .exec(http("Select").get("/computers/6"))

execの引数にexecを入れて記述する

コードを共通化したい時、この記述方法が必要となります。
メソッドチェーンは便利ですが、クラスでexecの処理を分けたい時は役に立ちません。
上のサンプルは、以下のように書くことも出来ます。execを複数のテストコードで使い回すことが可能になります。

object Search {
  val home = exec(http("Home")
  val search = exec(http("Search").get("/computers?f=macbook"))
  val select = exec(http("Select").get("/computers/6"))
}

val search_exec = exec(Search.home, Search.search, Search.select)

同じexec処理を10回実行したかったら、こう書けばすみます。

val pattern = exec(Search.home, Search.search, Search.select)
val search_exec = repeat(10){exec(pattern)}

exec の内部で使う変数を定義したい

Session.set、もしくは Session.setAll を使います。変数は ${foo}のように取り出せます。
Scalaなので、setAllの時は_を使ってSession変数を省略すると綺麗に書けます。

val session_set = exec(session => {
  val session1 = session.set("foo", "FOO")
  val session2 = session1.set("bar", "BAR")
  session2
})

val session_setall = exec({_.setAll(("foo", "FOO"), ("bar", "BAR"))})

val session_exec = exec(session_setall).exec(http("Select").get("/computers/{$foo}"))

注意点としては、setを行ごとに書くときは、毎回必ず変数化しましょう。
うっかり下のように書くと、当たり前ですが初めの"foo"はsetされません。(自分はこれ地味にハマりましたorz)

val session_set = exec(session => {
  session.set("foo", "FOO")
  session.set("bar", "BAR")
})

Sesionの中身を取り出したいときは、Session.getを使います。

val session_setall = exec({
  session.setAll(("foo", "FOO"), ("bar", "BAR"))
}).exec(session => {
  val foo = session.get("foo")
  println(foo)
})

http.getをするとSessionに格納されている変数

以下の変数が使用できます。

  • bodyString
    • レスポンスボディ文字列
  • bodyBytes
    • レスポンスボディバイナリ
  • responseTimeInMillis
    • レスポンスタイム
  • latencyInMillis
    • レイテンシー

指定したBodyとHeaderとパラメータを渡したい

事前にBodyを生成してSessionに渡す必要があります。
XMLやJsonは、asXML, asJsonが使えます。

Headerは header()、パラメータはqueryParam()が使えます。

exec(session => {
  val body = "..."
  session.setAll(("uri", "http://..."), ("body", body))
}).exec({
  http(this.getClass.getName)
    .post("{$uri}"}
    .queryParam("hoge", 10)
    .body(StringBody("{$body}")).asJson
})

指定した秒数間隔で実行するパターンを検証したい

pace() を使います。1分間隔でAPIにアクセスさせたい! というようなパターンに役立ちます。
ちなみに pause() もありますが、こちらは単純なsleepなので全くの別物です。

pace(60 seconds).exec(...)

レスポンスのValidationをしたい

レスポンスのValidationにはcheck()を使います。
transformを使うと自由なvalidationを書くことが出来ます。
また、正規表現で抽出確認も出来ます。

exec(http("Home")
  .check(status.is(200 to 210))
  .check(responseTimeInMillis.transform(_ / 1000.0).greaterThan(10))
  .check(regex( """<result="ok"/>"""))

レスポンスを保存したい

レスポンスの保存にはcheck()を使います。
(え、保存なのにcheckだって? 凄い違和感ありますよね。私もそう思います。)
なお、ここでもtransformを使ってデータの加工が出来ます。

exec(http("Home")
  .check(status.is(200 to 210).saveAs("status"))
  .check(bodyString.transform(_.size).saveAs("size")))

Sessionに含まれてるかを確認したい。

Session.contains が便利です。

  if(session.contains("hoge")) {
    ...
  }

Sessionの中身で条件分岐したい

doif、doIfOrElse を使います。

.doIf("${myKey}", "myValue") {
   exec( http("...") ... )
}

exec内部でFailureが発生したら、その地点でKO扱いにして終了させる

AのAPIへアクセスし、そのレスポンスの情報を変数化してBのAPIにアクセスする場合、
AのAPIへのアクセスが失敗しレスポンスが取得できないと、未定義の変数が使われるためExceptionが発生します。

対策としては exitHereIfFailed を使い、その地点でFailureが確定してる場合exitするようにします。
exec処理の最後に毎回書くのをオススメします。

object Search {
  val home = exec(http("Home").exitHereIfFailed
  val search = exec(http("Search").get("/computers?f=macbook")).exitHereIfFailed 
  val select = exec(http("Select").get("/computers/6")).exitHereIfFailed 
}

Sessionの中身によって、OK/KOを決定したい。

残念ながら、exec(session => ...) の中ではOK/KOは決定できません。
checkメソッドでtransformを活用しまくり、validationで頑張りましょう。

execで乱数を使いたい

Sessionでsetすれば使えます。
以下、事前に乱数の最大値maxがSessionに渡っている例です。

val random = new Random()
val e = exec((session: Session) => session.set("randNum", random.nextInt(session("max").as[Int])))

Sessionの中身をprintしたい

as[] を使います。場合に応じて、StringやLongやIntなどにしましょう。

println("responseTime:", session("responseTimeInMillis").as[Long] / 1000.0)

実行シナリオ(アクセスパターン)

最後にシナリオのアクセスパターンを記述する必要があります。
アクセスするユーザー数が設定できるため、負荷の調整が出来ます。
シナリオは主に以下をよく使います。

  • atOnceUsers(10)
    • 1回のみ10ユーザーがアクセスします
  • rampUsers(10) over(5 seconds)
    • 5秒間の間に0から10ユーザーまで線形的に上昇しながらアクセスします
  • constantUsersPerSec(20) during(15 seconds)
    • 15秒間の間、一定で20ユーザー/秒でアクセスします
  • rampUsersPerSec(10) to(20) during(10 minutes)
    • 10分間の間に、10ユーザー/秒から20ユーザー/秒まで線形的に上昇しながらアクセスします

GatlingはわからなくなったらGitHubのコード読めばいいと思います。
特にテストコードが超役に立ちます。

https://github.com/gatling/gatling/blob/8c9ff719abb7dec50bddccc7456e4b4e7571fe72/gatling-http/src/test/scala/io/gatling/http/HttpCompileTest.scala

こんな風にテストコードにたくさん実記述例が書かれてるので、サンプルが欲しくなったらテストあさるのが一番のオススメです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした