JMeterの変数と関数について : 概要、便利な関数、ハマりどころ/便利どころ


負荷試験ツールとしてよく使われるJMeterですが、変数や関数を使っているとハマりどころが意外と少なくありません。自分のための整理も兼ねて、JMeterの変数と関数についてまとめてみました。

実行環境

  • Windows 7
  • JRE 1.8.0_151
  • JMeter 5.0 r1840935

1. JMeterにおける変数・関数の概要

JMeterには多くのエレメント(GUIモードで左ペインに表示されるアイテム1個が1エレメントです)が用意されていますが、そのエレメントの名前やパラメータなどを動的に設定するために変数や関数を利用することができます

書ける場所、書き方、展開のされ方

JMeterのテスト計画内の各エレメントの名前やパラメータなどに${HOGE}という文字列を設定すると、テスト実行中に当該エレメントを実行したタイミングで変数HOGEの値に展開されます。(変数への値の代入方法は後述)
また、JMeterではデフォルトでいくつかの関数が組み込まれており、変数と同様に関数名と引数を${__function(arg1,arg2)}という風に設定することで、その文字列が関数実行結果に置き換えられます。

ポイント

  • もし${HOGE}を含むエレメントが評価されたタイミングで変数HOGEが未定義の場合、置換は行われず"${HOGE}"という文字列のままエレメントが実行されることになります。
    • 関数についても同様で、${__function(arg1,arg2)}という文字列が評価されたが__function(arg1,arg2)という関数が存在しなかった場合(orエラーになった場合)、"${__function(arg1,arg2)}"という文字列のままエレメントが実行されます。
  • 関数には引数を渡すことができ、,で区切って複数の引数を渡すこともできます。引数は引用符で囲わずに指定するようになっており、途中に空文字を含む場合も囲う必要がありません。
    • ただし、文字列内にカンマを含む場合は\,とエスケープする必要があります。
    • また、引数の最初・最後の空白は無視されます。
    • つまり、以下の全ては全て第1引数がFixed, 1st String、第2引数がSecondStringになります。
      • ${__function(Fixed\, 1st String,SecondString)}
      • ${__function(Fixed\, 1st String ,SecondString )}
      • ${__function( Fixed\, 1st String, SecondString)}
      • ${__function( Fixed\, 1st String , SecondString )}
  • 前処理/後処理などのスクリプティングが行われるエレメントに対しても先行して置換されます。詳しくは後述

変数への値の代入方法

変数への値の代入は、テスト計画内の各種エレメントで行うことができます。代表的なものは以下でしょうか。

  • シナリオ実行ごとにCSVを読み込んで利用する
    • 「CSV Data Set Config」エレメントでCSVのファイル名や読み込み方、変数への割り当てを設定すると、エレメントの実行ごとにCSVを1行読み進め、指定した変数名にcsvの値を代入してくれます。
    • 変数名は「Variable Names」に指定します。なお、${}で囲う必要はありません。
    • 使用例 :
      • データベースに存在する負荷試験対象ユーザのID・パスワードをCSVとして準備しておき、シナリオ実行1回につきCSVを一行読んでシナリオを実行することで大量ユーザによるログイン等の負荷を試験する
  • 「後処理」カテゴリのエレメント内で抽出して利用する
    • 「正規表現抽出」「JSON Extractor」など、HTTPサンプラーで取得した結果に対する後処理の抽出条件を設定して、抽出された文字列を指定した変数に代入することができます。
    • 代入先の変数名は、要素の「参照名」もしくは「Names of created variables」に指定します。CSVの時と同じく、${}で囲う必要はありません。
    • 使用例 :
      • ログイン系のリクエストに対するレスポンスから正規表現でアクセストークンを取得して変数に代入し、以降のサンプラーのパラメータとして使用する
      • レスポンスコードを変数に設定し、Ifコントローラの条件に利用して分岐させて後続シナリオを実装する1
  • 「テスト計画」要素や「ユーザ定義変数」設定エレメント内で設定して利用する
    • テスト計画の最上位エレメントである「テスト計画」エレメント内の「ユーザ定義変数」や、「設定エレメント」カテゴリの「ユーザ定義変数」エレメントで値を設定することができます。[追加]ボタンを押すとユーザ定義変数のテーブルに空行が追加されるので、[名前] [値]それぞれに変数名と値を入力します。
    • 使用例 :
      • パラメータを動的に設定するというより、「複数のエレメント内で共通で使い回されるが、毎試験ごとに柔軟に変更したい」というパラメータに使用する場合に適しています。
  • 関数の実行結果を代入する
    • 関数によっては、引数として「関数の代入先となる変数名」をオプションに指定できるものがあります。使いたい関数が変数への代入を可能かどうかは後述の「関数ヘルパーダイアログ」で確認するとよいでしょう。
  • BeanShellなどのスクリプティングによる前処理で設定する
    • JMeterのエレメントには、BeanShellやJavaScriptでスクリプトを記述できるエレメントが用意されています。長くなるのでここでは詳細に説明しませんが、各種スクリプト内でvars.put("変数名","値")という記述を行うことで変数に値を代入することができます。なお、スクリプト内ではvars.get("変数名")により変数を取得することができます。

変数が意図通りに代入されているかどうかの確認 : Debugサンプラ―の利用

シナリオ作成時、意図した通りに変数や関数が設定されているかどうかを確認したい場合があります。最もシンプルな方法として、Debugサンプラーによる確認がオススメです。

Debugサンプラーは、HTTPリクエストなどの外部リクエストを行わず、当該サンプラー実行時のJMeter変数/JMeterプロパティ/システムプロパティを応答データとして返してくれる便利なサンプラ―です。テスト計画の直下やスレッドグループ内に「結果をツリーで表示」リスナーを配置したうえで、変数の状態を確認したい場所にDebugサンプラ―を配置しておくと、サンプラ―実行時点での変数やプロパティを名前=値の形式で全て出力してくれます。

関数が意図通りに実行されているかどうかの確認 : __log関数の利用

後でも紹介するJMeterの組み込み関数__log(arg)を利用するのが便利です。__log(arg)関数は渡した引数の中身を評価してログ出力しつつ、関数自身の実行結果として引数の中身そのものを返してくれます。つまり、実行結果に影響を与えずに2実行中のログを記録することが可能となります。以下に例を示します。

jmeter.log
2018-12-04 00:28:23,332 INFO o.a.j.f.LogFunction: スレッドグループ(1スレッド・1ループ) 1-1  :  Debug Sampler@1 : ${ResponseCode}
2018-12-04 00:28:23,359 INFO o.a.j.f.LogFunction: スレッドグループ(1スレッド・1ループ) 1-1  :  Debug Sampler@2 : 200
2018-12-04 00:28:23,360 INFO o.a.j.f.LogFunction: スレッドグループ(1スレッド・1ループ) 1-1  :  Debug Sampler@3 : 392fe7a2-df8a-46b8-a966-be6f14c3091f

このように、実行されたスレッドグループとスレッドの番号、そして__log関数に渡された中身が評価されたものがJmeterのログに出力されます。
本当はDebugサンプラーでこういったことができると良いのですが、Debugサンプラーには引数を渡すことはできないため関数の実行結果を直接観測することはできません。そのため、BeanShell等のスクリプトを記述せずにJmeter関数で完結する範囲で関数の実行結果を確認する方法としては、この__log関数による確認が最もストレートな方法なのではないでしょうか。

2. 便利な関数とその使いどころ

JMeterには多数のビルトイン関数がありますが、その一覧は当然公式ドキュメント内でリファレンスとして提供されているほか、後述する関数ヘルパーダイアログでその動作を確認することができます。本稿では、関数ヘルパーダイアログによる関数の説明の確認方法・動作確認方法を説明するとともに、特に多用する関数について簡単に説明します。

関数について困ったら : 関数ヘルパーダイアログ

JMeterの関数について困ったら、公式ドキュメント以上に関数ヘルパーダイアログが役立ちます。[オプション]→[関数ヘルパーダイアログ](ショートカットキーは Ctrl+Shift+F1)で立ち上がるこのダイアログは、

  • 確認したい関数をリストボックスで一覧できる
  • 関数を選択すると、引数1つ1つに対する説明に加え、動作確認用に値を入力できる入力欄が出現する
  • 各引数に値を入力して[生成]ボタンを押すと、
    • その引数で実行する際の文字列(=エレメント内の設定したい箇所にコピペして使える)が生成され、
    • その引数を与えた場合の関数の実行結果と、実行後のJMeter変数の一覧(Debugサンプラ―で見えるものと同様)が確認できる

という機能を備えています。関数で困ったらまずはコレを試すことをオススメします。

便利な関数1 : __time

たぶん最も利用されることが多いのではないでしょうか。名前から推測できるとおり、関数を実行したタイミングでの時刻を出力してくれる関数であり、

  • 引数なしで実行→マイクロ秒でのUNIX TIMEで現在時刻を出力
  • 引数1つで実行→引数で指定したフォーマットで現在時刻を出力
    • フォーマットはJavaのSimpleDateFormatに則る。公式ドキュメントの例を引用すると、2018年1月21日1時23分45秒に実行した場合以下のように出力される
      • ${__time(dd/MM/yyyy,)}21/01/2018
      • ${__time(YMD,)}20180121
      • ${__time(YMDHMS,)}20180121-012345

というものです。

便利な使い道1 : .jtlファイルのファイル名にタイプスタンプを付与し、過去実施時のログへの追記を回避する

JMeterのリクエスト結果を逐次記録していくリスナーである「結果をツリーで表示」「統計レポート」などのリスナーエレメントは、出力先ファイルに指定したファイル名と同名のファイルが存在する状態で実行すると(GUIモードの場合を除き)自動的に既存ファイルに追記してしまいます。
過去に実行したJMeterでの負荷ショットへの追記を防ぐため、出力ファイル名に__time関数でタイムスタンプを付与することで、負荷ショット実行ごとに自動でファイルを分けてログを出力させることができます。(例 : logtree_${__time(yyyy-MMdd-HHmm)}.jtl)

便利な使い道2 : getパラメータへのタイムスタンプ付与によるキャッシュ避けを再現する

「CDNやHTTPサーバなどでキャッシュさせないために、URL内に(リクエスト内容に影響のない範囲で)異なるgetパラメータを付与する」ということはしばしば行われるのではないでしょうか。3同様のことを、

  • HTTPサンプラーがgetリクエストであれば、getパラメータに_ : ${__time}を設定
  • HTTPサンプラーがpostリクエストであれば、のパス末尾に?_=${__time}のようにタイプスタンプを付与する

といった方法で再現することが可能になります。

便利な関数2 : __P (=__property)

「ショットごとに向き先のホストを変更したい」「ショットごとに特定シナリオのスレッド数・ループ回数を変更したい」などの場合、実行の度にjmxファイルを修正するのは面倒です。そんなときに使えるのがこの__P(__propertyの省略形です)関数で、JMeterの起動パラメータとして渡した値を参照することができます。

詳細はこちらのエントリなどが参考になったので、こちらに譲ります。

便利な関数3 : __log

関数の実行結果のロギングに便利と述べた__log関数ですが、関数の実行結果のモニタリングだけでなく、
「あるエレメントのある設定項目が、JMeterの変数・関数として評価されるか?」ということの確認にも使えます。
たとえば

  • 「HTTPサンプラー」などのサンプラ―エレメントの名前に__logを仕込む
    • →ログに記録されるので、ここが関数・変数の展開可能箇所だとわかる
  • 「正規表現抽出」エレメントの名前に__logを仕込む
    • →ログに記録されないので、ここが関数・変数として展開されない箇所だとわかる

といったことができます。__counter関数など、関数を叩いたタイミングや叩かれた箇所・回数などについて確認したいときに使えそうです。

3. ハマりどころ/便利どころ

ハマりどころ : スクリプティング系のエレメント内における処理順序

「後処理」カテゴリなどで利用されるBeanShell・JavaScript等によるスクリプトにおいては、

  • まず最初に${}に対する展開が行われ、
  • その結果として得られたスクリプトが実行される

という順序で処理が行われます。また、その際は${}""''で囲われていても展開されます。このことに注意してスクリプトを実装しないと、型エラー等に惑わされて苦しむことになります。
もっとも、スクリプト内で変数を参照・設定する場合はvars.get/vars.putを使えるので、そちらを使う方がトラブルが少なく済むのではないかとも思われます。

便利どころ : 特定の変数をリスナーのログに出力する方法

負荷試験において、「使えるユーザを何分の負荷で何ユーザ消費したか?」という確認を行いたい場合や、エラーが発生したユーザを特定したいなどの場合があります。そんな時は、出力対象としたい変数名をjmeter.propertiesやJmeterの実行時のオプションに指定することで、「統計レポート」等のリスナーで出力するサンプラ―毎のログにその変数を追加することができます。具体的に、var1var2の2つの変数を追加したい場合は

  • JMeterの起動オプション(jmeter.bat)に-Jsample_variables=var1,var2を追加する
  • jmeter.propertiesにsample_variables=var1,var2と記述する

のいずれかの方法でこれを行うことができます。

ハマりどころ : 変数のスコープについて

変数のスコープについても説明しようと思いましたが、それだけで結構な分量になってしまうのでそのうち別記事で投稿します。(WIP)


  1. 一般的な負荷試験においては、1つのユーザ行動フローを1つのスレッドグループ(=1つのシナリオ)に割り当てるのが真っ当な筋であり、Ifコントローラを使って行動フローを分岐させるというのはあまり推奨されるものではありません。ですが、例えば「同一URLへのリクエストのうち、一定割合のリクエストをエラーにするような流量制御を行う」などの処理がある場合、例に示したような使い方が活躍するかもしれません。もっとも、そこまでのものを作る場合はJMeterよりGatlingやLocustなどの負荷ツールを検討した方がいいのかもしれませんが…… 

  2. 機能的には影響はありませんが、性能的な影響はないとは言えません。デバッグ時のみの利用にとどめ、目標とする負荷量でJMeterを実行する際にはログ出力を外しておくのが堅実です。 

  3. 例えば、jQueryでajax通信を行う際、jQuery.ajax(){cache:true}のパラメータを与えると、ajaxリクエストURL末尾に&_=1516540541のように文字列が付加されることによりキャッシュ避けが行われます。