はじめに

JMeterで性能テストをしていて、ExcelファイルのアップロードだけどうしてもJMeterで再現できず、手動で計測していたことがありました。今回改めて性能テストを担当することになり、試行錯誤した結果JMeterで自動化できたので、その方法をまとめます。

何が難しいのか

ブラウザの操作だと「ファイルを選んで送信ボタンを押す」だけですが、これをJMeterで再現しようとすると意外とハードルがあります。

ハードル 内容
認証 OTP認証があるとJMeterだけではログインできない
ファイル送信 通常のPOSTとは違い multipart/form-data で送る必要がある
パラメータ引き継ぎ 画面遷移が複数ステップある場合、前の画面の値を次に渡す必要がある
並列実行 スレッドごとに異なるファイルを送りたい

以降、それぞれどう解決したかを紹介します。

1. OTP認証をどうするか

問題

OTP(ワンタイムパスワード)認証はJMeterで自動化できません。毎回違うコードが発行されるので、ログインのリクエストをそのまま再現しても通りません。

解決策

ブラウザで先にログインして、そのセッションIDをJMeterに使わせる方式にしました。

① ブラウザでログイン(OTP認証を通す)
② 開発者ツール(F12) → Application → Cookies → JSESSIONIDの値をコピー
③ JMeterにその値を設定
④ JMeterはログイン済みのセッションとしてリクエストを送信

JMeterの設定

ユーザー定義変数 にコピーした値を設定:

変数名
JSESSIONID ブラウザからコピーした値

HTTP Cookie Manager でリクエストに載せる:

項目
名前 JSESSIONID
${JSESSIONID}
ドメイン 対象サーバーのホスト名
Secure true(HTTPS環境の場合)

注意: セッションタイムアウトがあるので、取得後は速やかにテストを実行すること。

2. Excelファイルのアップロード

問題

ブラウザでファイルをアップロードするとき、裏では multipart/form-data という特殊な形式でファイルデータを送信しています。JMeterの通常のPOSTリクエストではこの形式にならないので、そのままではサーバーがファイルを受け取れません。

解決策

HTTPリクエストの設定で2つのことをします。

やること1: multipart/form-data を有効にする

HTTPリクエストの画面にある 「Use multipart/form-data」チェックボックスをON にします。これだけで、JMeterがファイル添付モードでリクエストを送るようになります。

やること2: アップロードするファイルを指定する
HTTPリクエストの 「Files Upload」タブ で、以下の3つを設定します。

項目 設定値 説明
ファイルパス ${excel_file_path} アップロードするファイルの場所
パラメータ名 uploadFile サーバー側が受け取るパラメータ名(HTMLの のname属性に合わせる)
MIMEタイプ application/vnd.openxmlformats-officedocument.spreadsheetml.sheet .xlsx のMIMEタイプ(.xls なら application/vnd.ms-excel

ファイルと一緒にhidden項目などの通常パラメータも送りたい場合は、「パラメータ」タブに追加すればOKです。

3. 画面遷移のパラメータ引き継ぎ

問題

アップロード処理が1回のリクエストで完結せず、複数ステップに分かれていることがあります。

Step1: ファイル送信 → 確認画面が返ってくる
Step2: 確認画面で確定 → 登録処理
Step3: 登録確定 → 完了

この場合、Step1のレスポンスHTML内にあるhidden項目の値(例: ファイルの一時保存キー等)をStep2のリクエストに含める必要があります。ブラウザなら自動で送られますが、JMeterでは自分で抽出して渡す必要があります。

解決策: 正規表現抽出

Step1のHTTPリクエストの子要素として正規表現抽出を追加し、レスポンスHTMLから値を取り出します。
例えば、レスポンスに以下がある場合

<input name="s3Key" type="hidden" value="abc123" />
項目 設定値
参照名 s3Key
正規表現 name="s3Key"[^>]*value="([^"]*)"
テンプレート $1$
一致番号 1

これで上記の例だと value="abc123"abc123 部分が抽出され、後続のリクエストで ${s3Key} として参照できるようになります。

件数が可変の配列パラメータの場合

hidden項目が sheetNames[0], sheetNames[1], … のように件数が決まっていない配列の場合は、正規表現抽出だけでは対応できません。BeanShell PreProcessorを使って動的にパラメータを組み立てます。

BeanShell PreProcessorとは?
HTTPリクエストを送る直前にJavaコードを実行できるJMeterの機能です。通常の設定画面だけでは対応できない処理(パラメータの動的な組み立て、条件分岐など)が必要なときに使います。

ステップ1: 正規表現抽出で全件取得する

項目 設定値
参照名 sheetName
正規表現 name="sheetNames\[\d+\]"\s+value="([^"]*)"
一致番号 -1(全件抽出。デフォルトの1だと1件しか取れない)

-1 を指定すると、sheetName_1, sheetName_2, … と sheetName_matchNr(件数)が変数にセットされます。
ステップ2: BeanShell PreProcessorでリクエストパラメータに追加する
JMeterの画面左側のツリーで、対象のHTTPリクエストを右クリック →「追加」→「前処理」→「BeanShell PreProcessor」を選択します。追加すると右側にテキストエリア(Script欄)が表示されるので、そこに以下のJavaコードを書きます。

やっていることは「正規表現抽出で取り出したシート名を、次のリクエストのパラメータとして追加する」です。

import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.http.util.HTTPArgument;

// 正規表現抽出で取得したシート名の件数を取得
String countStr = vars.get("sheetNames_count");
if (countStr != null) {
    int count = Integer.parseInt(countStr);
    // これから送るHTTPリクエストのパラメータを取得
    org.apache.jmeter.config.Arguments args = sampler.getArguments();
    // 抽出した値を1件ずつリクエストパラメータに追加
    for (int i = 0; i < count; i++) {
        String val = vars.get("sheetNames_" + i);
        if (val != null) {
            HTTPArgument arg = new HTTPArgument(
                "sheetNames[" + i + "]", val);
            arg.setAlwaysEncoded(true);
            args.addArgument(arg);
        }
    }
}

これでリクエストに sheetNames[0]=Sheet1&sheetNames[1]=Sheet2&... が動的に追加されます。

4. 並列実行時のファイル割り当て

問題

並列度5でテストしたいとき、5スレッド全部が同じファイルを送ると実態に即しません。スレッドごとに異なるファイルを送りたい。

解決策

CSV Data Set Config でスレッドごとにファイルパスを割り当てます。
まずCSVファイルを用意:

excel_file_path
/path/to/upload_1.xlsx
/path/to/upload_2.xlsx
/path/to/upload_3.xlsx
/path/to/upload_4.xlsx
/path/to/upload_5.xlsx

CSV Data Set Configの設定:

項目 設定値
ファイル名 CSVファイルのパス
変数名 excel_file_path
最初の行を無視 true
リサイクル false
共有モード 全スレッド

あとはHTTPリクエストのファイルパスに ${excel_file_path} を指定するだけです。スレッド1→1行目、スレッド2→2行目…と自動的に割り当てられます。

テストプラン全体の構成

最終的なテストプランの構成です。

テストプラン
├── ユーザー定義変数(ホスト名, JSESSIONID 等)
├── スレッドグループ(並列度5、ループ1回)
│ ├── CSV Data Set Config(ファイル割り当て)
│ ├── HTTP Cookie Manager(JSESSIONID)
│ ├── HTTP共通設定(ホスト、ポート、プロトコル)
│ │
│ ├── Step1【HTTPリクエスト】
│ │ ├── multipart/form-data + ファイルアップロード
│ │ └── 正規表現抽出(hidden項目を抽出)
│ │
│ ├── Step2【HTTPリクエスト】
│ │ ├── BeanShell PreProcessor(配列パラメータ組み立て)
│ │ └── 正規表現抽出(次ステップ用)
│ │
│ ├── Step3【HTTPリクエスト】
│ │
│ └── リスナー(結果ツリー、統計レポート)

ハマったポイント

multipart/form-data のチェック忘れ

ファイルアップロードの設定をしても、「multipart/form-dataを使用」にチェックが入っていないとただのPOSTになります。見た目は地味なチェックボックスなので見落としやすいです。

MIMEタイプの指定漏れ

MIMEタイプを空にするとサーバー側でファイルとして認識されないことがあります。.xlsx は長いですが正確な指定が必要です。

application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

正規表現抽出の一致番号 -1

配列パラメータを全件抽出するには一致番号を -1 にします。デフォルトの 1 だと1件しか取れません。

BeanShellのインデックスのズレ

正規表現抽出の変数は sheetName_1 から始まる(1始まり)のに対し、サーバーに送るパラメータはsheetNames[0]から始まる(0始まり)です。変換を忘れるとサーバー側でパラメータが受け取れません。

まとめ

  • OTP認証がある環境でも、ブラウザからJSESSIONIDを取得してCookie Managerに注入すればテスト可能
  • Excelアップロードmultipart/form-data チェックON + MIMEタイプの正確な指定がポイント
  • 複数ステップの画面遷移は正規表現抽出でパラメータを引き継ぐ
  • 可変の配列パラメータはBeanShell PreProcessorで動的に組み立てる
  • 並列テストはCSV Data Set Configでスレッドごとにファイルを割り当て
    「JMeterでファイルアップロードはできない」と思っていましたが、やってみたら設定の組み合わせで実現できました。同じような状況で困っている方の参考になれば幸いです。