経緯
業務でパフォーマンステストのためにgatlingでテストシナリオを書くことがあるため
gatlingを使用しているとexec関数の部分がネストにより複雑化するため、
既存のStructureBuilderクラスを拡張してネストする部分を隠蔽化してみる
exec関数のネスト問題
gatlingではexec関数を使用してメソッドチェーンの形式でコードを書いていきます。
下記のコードはgatlingでログインとログアウトする場合を考えたシナリオです。
この例はネストする部分が短いですが、gatlingではexec関数の中にさらにexec関数を書けるため複雑なテストシナリオを書こうとするとネストが深くなりコードが複雑化します。
class MySimulation extends Simulation {
val httpProtocol = http
.baseUrl("http://localhost:8080")
val id: String = "testUser"
val pass: String = "hello"
val scn = scenario("MySimulation")
.exec(http("login")
.post("/login")
.body(StringBody("""{ "id": """" + id +"""", "pass": """" + pass +"""" }""")).asJson
.check(status.is(200))
.pause(1)
.exec(http("logout")
.post("/logout")
.check(status.is(200))
setUp(scn.inject(rampUsers(100) during (10 seconds)).protocols(httpProtocol)
}
解決策
Scalaにはimplicitという修飾子があり、implicit classを使用することで既存のクラスを拡張することができます。
そこでgatlingの既存クラスのStructureBuilder自体にログインとログアウトする処理を直接追加し、ネストする部分をここに隠蔽化します。
下記がStructureBuilderを拡張したコードになります。
package test
import io.gatling.core.structure.StructureBuilder
object MyScenario {
implicit class StructureBuilderWithWhatevers[B <: StructureBuilder[B]](b: B) {
def login(id: String, pass: String): B = {
b.exec(http("login")
.post("/login")
.body(StringBody("""{ "id": """" + id +"""", "pass": """" + pass +"""" }""")).asJson
.check(status.is(200))
}
def logout(): B = {
b.exec(http("logout")
.post("/logout")
.check(status.is(200))
}
}
StructureBuilderを拡張したMySenarioを使用するために使用側のクラスでimportします。
import test.MyScenario._
class MySimulation extends Simulation {
val httpProtocol = http
.baseUrl("http://localhost:8080")
val id: String = "testUser"
val pass: String = "hello"
val scn = scenario("MySimulation")
.login(id,pass)
.pause(1)
.logout()
setUp(scn.inject(rampUsers(100) during (10 seconds)).protocols(httpProtocol)
}
こうすることによって追加したログイン,ログアウトする関数を使用側のクラスでメソッドチェーンで簡潔に記述できるようになり、ログインとログアウトの処理も再利用しやすくなりました。