(2022-03-03追記) 本記事で取り上げた内容は #4157 で解決され、Gatling 3.7 で取り込まれました。Gatling 3.7 以降の EL 式では、${}
の代わりに #{}
を利用することが推奨されています。1
Gatling は Scala でテストコードを記述する負荷試験ツールですが、その多くを占めるシナリオ部分は Gatling DSL と呼ばれるドメイン特化言語で記述します。
Gatling DSL の EL 式(Expression Language)
Gatling DSL
の EL 式は Gatling のセッション属性(Session attributes
)への参照を埋め込む際に利用します。
// セッション属性 foo に値を設定する
session.set("foo", "FOO")
// Session API でセッション属性 foo の値を取り出す
session("foo").as[String] // FOO
// EL 式でセッション属性 foo の値を取り出し、文字列に埋め込む
"${foo} BAR" // FOO BAR
基本的には ${属性名}
という記法で参照を得ますが、EL 式に組み込まれた機能を利用してもっと複雑なこともできます:
// コレクション foo のサイズを得る
"${foo.size()}"
// インデックス付コレクション foo からランダムな要素を一つ取り出す
"${foo.random()}"
// インデックス付コレクション foo から 5 番目の要素を一つ取り出す
"${foo(5)}"
// インデックス付コレクションもしくはタプルである foo から n 番目の要素を取り出す
"${foo(n)}"
// タプル foo から 2 番目の要素を取り出す
"${foo._2}"
// マップ foo からキーが bar である要素を取り出す
"${foo.bar}"
// foo があれば true を、なければ false を得る
"${foo.exists()}"
// foo がなければ true を、あれば false を得る
"${foo.isUndefined()}"
// foo を JSON 形式でフォーマットした値を得る
"${foo.jsonStringify()}"
Gatling DSL
の EL 式は Gatling DSL
の関数に渡される String に対してのみ動作します。
Scala の変数展開
一方、Scala には変数展開(あるいは「文字列の補完」、string interpolation
)という機能があります。文字列の前に補間子(interpolator
)をつけることで、変数へ参照を埋め込むことができます。
val foo = "FOO"
println(s"$foo BAR") // FOO BAR
基本的には $変数名
という記法で値を取り出しますが、${式}
という記法で任意の式を埋め込むこともできます:
println(s"${1 + 1}") // 2
Scala に組み込まれた補完子は三種類あります。
// s 補完子。シンプルな変数展開を実現します。
val foo = "FOO"
println(s"$foo") // FOO
// raw 補完子。s 補完子の機能に加え、エスケープを実行しません。
println(raw"{"foo":"$foo"") // {"foo":"FOO"}
// """~""" でも raw 補完子と同じ効果を実現します。
println("""{"foo":"$foo"}""") // {"foo":"FOO"}
// f 補完子。C の printf のようなフォーマット付文字列を実現します。
val decimal = 10
println(f"$foo%s $decimal%d") // FOO 10
混在するとなにがややこしいのか
さて、Scala の変数展開では、下のような記述も許されます:
s"${foo} BAR" // FOO BAR
"""${foo} BAR""" // FOO BAR
Gatling DSL の EL 式にそっくりですね。実際の所、Gatling DSL でも Scala の変数展開を利用することはできますが、それぞれの機能は排他的です:
session.set("foo", "FOO")
val bar = "BAR"
"${foo} BAR" // FOO BAR -- 1
s"FOO ${bar}" // FOO BAR -- 2
"${foo} ${bar}" // FOO ${bar} -- 3
s"${foo} ${bar}" // error! -- 4
3 や 4 のように変数とセッション属性の取得を同時に行いたい場合は、変数をセッション属性にセットするか、式展開で Session API
を呼ぶことで実現できます。