JMeterを活用したパフォーマンステストの実践ガイド

この記事は[NTTコムウェア AdventCalendar 2023](https://qiita.com/advent-calendar/2023/nttcomware) 22日目の記事です。

# はじめに
こんにちは、NTTコムウェアのD.K. Parkと申します。
テクニカルリードの業務に従事しています。本記事では、パフォーマンステストにおいて利用しているJMeterについてご紹介いたします。JMeterを活用することで、アプリケーションの性能評価やボトルネックの特定など、効果的なパフォーマンステストが可能となります。JMeterを使用したパフォーマンステストの実践的なガイドと注意点を共有します。詳細について以下でご説明いたします。

# 環境情報
JMeter 5.6.2
JDK version: 11/17

# 使い方
## Config Element
### User Defined Variables
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647569/f8ca85ad-ae73-1f7b-8d17-ae765ad5e795.png)
`User Defined Variables（ユーザー定義変数）`は、各スレッドに対して一連の定数を作成するテスト計画要素です。これらの変数は、テスト実行中に値が変更されませんが、他の要素から簡単に取得できます。これはテスト計画内で複数回使用する値を一度定義し、その後簡単に参照または再利用するための非常に便利な機能です。`User Defined Variables`機能は以下のように設定します。

1. テスト計画内で右クリックし、`"Add" -> "Config Element" -> "User Defined Variables"` を選択
2. User Defined Variablesエレメントが作成されます。ここに`「Name」（変数名）`と`「Value」（変数の値）`を指定します。
3. これらの変数は、以降のテスト計画内で `${変数名}`の形式で参照可能になります。

例えば、変数名 `“host"`, 値 `"stg.example.com"`の変数を定義した場合、HTTP Requestエレメントのサーバ名またはIPフィールドに`${HOSTNAME}`と書くことで`stg.example.com`と入力したものと同じになります。

:::note alert
注意点：
・User Defined VariablesはJMeter起動時に一度だけ評価されます。
・同じ名前のUser Defined Variablesが複数ある場合、一番下にあるものが優先
:::

### HTTP Request Defaults
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647569/0183a3c3-907d-9b1e-d44b-37845a170542.png)
`HTTP Request Defaults`機能は一連のHTTPリクエストが持つ共通のパラメータを定義するための構成要素です。これは、テスト計画内で複数のHTTPリクエストが同じサーバーへの接続、ポート番号、パスなどの共通要素を持っている場合に非常に役立ちます。HTTP Request Defaults 設定要素は以下のように作成します。

1. テスト計画内で右クリックし、`"Add" -> "Config Element" -> "HTTP Request Defaults"` を選択します。
2. 作成された`HTTP Request Defaults`エレメントに共通のパラメータを指定します。
例えば、サーバー名/IP、ポート番号、プロトコル(HTTP/HTTPS)などを指定します。
例）Protocol : `https`、Server Name or IP : `${host}`、Port Number : `443`
3. 一度設定すると、その下にあるHTTPリクエストがこの設定を継承します。
どのリクエストも個別に設定することなく、HTTPリクエストの共通要素を一元管理できます。

:::note alert
注意点：
・`HTTP Request`の設定が`HTTP Request Defaults`の設定と競合する場合、`HTTP Request`の設定が優先されます。
・この機能は、HTTPリクエストが多数あり、各リクエストで共通の設定（例：サーバー名/IP、ポート番号）がある場合に、その設定を一元管理できるため、非常に効率的です。
・なお、`HTTP Request Defaults`はサーバ名/IP、ポート番号やプロトコルなど、HTTPリクエストの共通の設定のみを保持します。
各リクエスト固有のパラメータ（例：パス、パラメータ、メソッド等）は個別のHTTPリクエスト設定にて指定する必要があります。
:::

### HTTP Cookie Manager
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647569/de73e798-8680-1e03-0faf-5e011dc2d97d.png)
`HTTP Cookie Manager`機能は、ウェブサイトがクッキーを使ってユーザーセッションの状態を追跡するのをシミュレートするための機能です。HTTP Cookie Managerは各ユーザー（スレッド）別にクッキーを保持し、HTTPリクエストとレスポンス間でクッキーを適切に送受信します。
これにより、ログイン状態の維持やアクセス制御などクッキーを用いた動作をテストすることができます。
また、HTTP Cookie Managerは手動でクッキーを追加することも可能で、特定のクッキーを設定した状態でのテストを行うことができます。
HTTP Cookie Managerの設定は以下のように行います。

1. テスト計画内で右クリックし、`"Add" -> "Config Element" -> "HTTP Cookie Manager"`を選択します。
2. 作成されたHTTP Cookie Managerエレメント内でクッキーの設定を行うことができます。
需要により手動でクッキーを追加したり、クッキーの削除ポリシーを設定したりすることも可能です。
なお、HTTP Cookie Managerは各ユーザー（スレッド）に対して独立して動作しますので、複数ユーザーのセッションを同時に管理するテストにも利用することができます。
基本的にはウェブブラウザのように自動でクッキーを扱ってくれますが、必要に応じて具体的なクッキーの値を指定したり、クッキーの挙動を制御するためのオプションを設定することもできます。

:::note info
参考） クッキー保持の確認方法：View Results TreeのRequest Body
:::

### HTTP Header Manager
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647569/e7cf830e-4312-3a44-1300-fb8ea8e62dbe.png)
`HTTP Header Manager`はHTTPリクエストにカスタムのHTTPヘッダーを追加できるようにする機能です。これはテスト中に特殊なヘッダーや認証トークン、カスタムユーザーエージェントなどを含める必要がある場合に便利です。使用方法は以下の通りです。

1. テスト計画に`HTTP Header Manager`を追加します。これは通常`HTTP Request`（またはその親のコントローラ）と同じレベルか、もしくはその下に追加されます。
右クリックして `"Add" -> "Config Element" -> "HTTP Header Manager"`を選択します。
2. 開いた`HTTP Header Manager`の画面で、任意の名前と値のペアでHTTPヘッダーを追加します。
よく使用されるヘッダーには `"User-Agent"`, `"Accept-Language"`, `"Authorization"`などがあります。
`HTTP Header Manager`で設定したヘッダーは、その下にあるHTTPリクエスト全てに適用されます。
複数の`HTTP Header Manager`が存在する場合、最も近い親のものが優先されます。
 
なお、特定のヘッダーが複数の`Header Manager`で指定されていた場合、一番子（下）にある`Header Manager`の値が優先されます。また、HTTPリクエストで個別にヘッダーが指定されている場合は、その値が最優先となります。
このように、`HTTP Header Manager`はJMeterのテスト計画で共通のHTTPヘッダーを設定したり、個別のリクエストで特殊なヘッダーを追加するのに便利なツールです。

## Non-Test Elements
### HTTP(S) Test Script Recorderとは
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647569/ba2b331a-8503-c033-377e-044e0e17d834.png)
`HTTP(S) Test Script Recorder`機能は、ユーザーがブラウザで行う操作をリアルタイムで記録し、それをJMeterのテスト計画として保存する機能です。
これにより、手動で多数のリクエストを作成する代わりに、実際のブラウザ操作を録画し、それを再生することで実際のユーザー操作をシミュレートすることが可能になります。
使用方法は以下の通りです。

1. まず、`Test Plan（テスト計画）`内で右クリックし、各メニューから `"Add" -> "Non-Test Elements" -> " HTTP(S) Test Script Recorder"`を選択します。
2. 設定画面で`Port（ポート）`を指定します。このポートは後続のステップでブラウザのプロキシ設定に使用します。
3. `"Start"`ボタンを押すと、指定したポートでリスニングを開始します。その後、ブラウザのプロキシ設定をJMeterのIPアドレスと設定したポートに変更します。
4. ブラウザでアクセスしたいウェブサイトやページを開きます。この操作は全てJMeterによって記録され、テスト要素として追加されます。
5. テスト計画に必要な操作が全て終わったら"Stop"ボタンを押します。

:::note alert
注意点：
・`HTTP(S) Test Script Recorder`を使用すると、JMeterはプロキシサーバとして動作します。そのため、ブラウザの接続設定にプロキシサーバを設定する必要があります。
・HTTPSのサイトを記録する場合、JMeterのルートCA証明書をブラウザにインストールする必要があります。
・フィルタリング設定しない場合、不要な要素（CSS、イメージ、JavaScript）も含まれてしまいます。下記の設定で必要なリクエストだけ残します。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647569/b7b56bfe-24df-95c3-1aca-5c918b0ba21a.png)

:::note info
参考）Request Filtering 設定
URL Patterns to Include（含まれるURLパターン設定）
「`.*test.example.com.*`」
URL Patterns to Exclude（除外するURLパターン設定）
「`(?i).*\.(bmp|css|js|gif|ico|jpe?g|png|swf|woff|woff2|txt|html|lp|ws|svg).*`」
「`(?i).*(/g/collect|/sonota/reigai/).*`」
:::

### HTTP(S) Test Script Recorder設定方法
JMeterは`Prоxу Server`として機能し、HTTP/HTTPSリクエストを記録（capture）することができます。このためには、使用しているブラウザ（この場合Firefox）で適切なプロキシ設定を行う必要があります。
また、HTTPSリクエストを記録する場合は、JMeterのルート証明書をブラウザにインストールすることも必要です。
これはJMeterがブラウザとサーバ間で`"Man-In-The-Middle"`の役割を果たし、HTTPS通信を傍受・復号するためです。
以下に、JMeterのプロキシと証明書の設定手順を供述します。

:::note warn
① プロキシ設定
:::
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647569/cafe1fd7-e709-9aa5-3a11-0994ee6c8d7b.png)
1. Firefoxを開き、右上のメニューアイコンをクリックします。
2. `"Options"`または`"Preferences"`を選択します。
3. `"Network Settings"`のセクションを探し、`"Settings"`ボタンをクリックします。
4. `"Manual proxy configuration"`を選択し、HTTP Proxyフィールドに`“127.0.0.1"`、PortフィールドにJMeterがリスニングしているポート番号（デフォルトは`8888`）を入力します。
5. `"Also use this proxy for HTTPS"`をチェックし、`"OK"`をクリックします。

:::note warn
② 証明書の設定
:::
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647569/e32f050d-3132-c8e1-957a-2213ef2bfe41.png)
1. JMeterを起動し、`"Options"`メニューから`"SSL Manager"`を選びます。（なければ次に進んでください）
2. “bin”ディレクトリ内にある`“ApacheJMeterTemporaryRootCA.crt”`ファイルを探します。
 該当するファイルがない場合、一度`HTTP(S) Test Script Recorder`を開き、
 `"Start"`をクリックしてから再度探します。
3. Firefoxで`"Options"`または`"Preferences"`へ移動し、`"Privacy & Security"`を選択します。
4. ページ下部にある`"Certificates"`セクションで`"View Certificates"`をクリックします。
5. `“Authorities”`タブを選択し、`“Import”`ボタンをクリックします。
先ほどの`“ApacheJMeterTemporaryRootCA.crt”`ファイルを選択し、
`"Trust this CA to identify websites."`をチェック後、"OK"ボタンをクリックします。
 
以上で設定は完了です。
これでFirefoxブラウザを通じて行う全てのHTTP/HTTPSリクエストをJMeterが記録できる状態になります。

## Threads (Users)
### Thread Group
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647569/ade8886c-40a0-58f3-eaa6-3215dcefee86.png)
`Thread Group`はパフォーマンステストでは非常に重要なコンポーネントで、テストの実行計画とロードの生成方法を定義します。
`Thread Group`を設定することで、以下のような情報を指定します。

```
・Number of Threads (users)　: 実行するスレッド（ユーザー）の数を指定。一つのスレッドは一つのユーザーをシミュレートします。
・Ramp-up period (seconds)　: 指定したスレッドがすべて開始するまでの時間（秒）を指定。この期間中に指定したスレッド数が均等に開始します。
・Loop Count　: テストのイテレーション（反復）回数を指定。“Infinite"をチェックすると無限にテストが繰り返されます。
 ```
 
例えば、「`Number of Threads`」を10に、「`Ramp-Up Period`」を20秒に設定した場合、20秒間に10ユーザーが均等にスタートします。つまり、2秒に1ユーザーがテストを開始します。
そして、「`Loop Count`」で設定した回数だけそのテストを繰り返して負荷をかけます。
`Thread Group`は、複数のHTTPリクエスト（または他のリクエストタイプ）、リスナー、アサート、タイマーなどを子要素として持つことができます。
これにより、各ユーザー（スレッド）が実行する連続したテスト計画を定義することが可能です。また、`Thread Group`はテスト計画のスタートポイントとなるため、一つのテスト計画に必ず一つは`Thread Group`を含める必要があります。

## Sampler
### HTTP Request
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647569/27a31e5b-8fd7-dbdc-eb93-0604e3fe09d7.png)
`HTTP Request`は、HTTPプロトコルを通じてウェブサーバーにリクエストを送信するための主要なテスト要素です。各`HTTP Request`は一つのHTTPリクエスト（GET、POST、PUT、DELETE等）をシミュレートし、相手のウェブサーバーからレスポンスを取得します。
`HTTP Request`設定要素は以下のパラメータを指定することができます。

```
・Server Name or IP：接続先サーバのドメイン名またはIPアドレス
・Port Number：接続先サーバのポート番号
・Protocol：使用するHTTPプロトコル（HTTPまたはHTTPS）
・Method：HTTPメソッド（GET、POST、PUT、DELETEなど）
・Path：リクエストするパス（例えば、"/"や"/index.html"など）
・Parameters：HTTPリクエストに送信するパラメータ。これはクエリ文字列またはPOSTリクエストボディとなります
```
 
`HTTP Request`はHTTPリクエストを送信し、そのレスポンスを受け取るまでの時間（= レスポンスタイム）を計測します。
また、サーバからのレスポンス（HTTPヘッダ、本文等）を検証するためにAssertionsを追加することができます。
`HTTP Request`は、Webページへのアクセス、Web APIの呼び出し、ログイン、データ送信など、さまざまなWebテストシナリオを表現するために利用されます。
また、これらのリクエストは`Thread Group`や他のコントローラでグルーピングして、複数のユーザー、異なる順序、条件付きの実行などを模擬することができます。

## Post Processors
### Regular Expression Extractor
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647569/1f5a3a04-24d5-f54c-83c5-dd98fa0b5224.png)
`Regular Expression Extractor`は、サーバからのレスポンスから特定の情報を抽出するためのポストプロセッサです。
正規表現を使用して、レスポンスデータから特定の値を取り出し、それを後続のリクエストで利用することができます。
例えば、一つ目のHTTPリクエストでセッションIDを取得し、それを二つ目のリクエストで使用するといったケースで使われます。
以下は、`Regular Expression Extractor`の主な設定項目です。

`Field to check`：レスポンスデータのどの部分から値を抽出するかを指定します。
`Body`、`Body (unescaped)`、`Body as a Document`、`Request Headers`、`Request Headers`、`URL`、`Response Code`、`Response Message`から選ぶことができます。

```
・Name of created variable：抽出した値を保存する変数の名前を指定。この変数は後続のサンプラーから${変数名}の形式で参照可能
・Regular Expression：値を抽出するための正規表現パターンを指定
・Template：マッチング部位からどの部分を取り出すかを指定。数字で指定し、$1$は最初の一致部分を表します
・Match No.：この数値によって抽出するマッチング結果を指定。0を設定するとランダムに抽出される
・Default Value：正規表現にマッチするものがない場合にデフォルトで設定される値を指定
```

このように、`Regular Expression Extractor`はレスポンスデータから特定の情報を抽出して再利用するための強力なツールです。
動的なセッションIDやトークン、ユーザー特定のデータなどを扱うシナリオのテストで頻繁に用いられます。

## Listener
### View Results Tree
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647569/2bd59b87-d479-c139-6729-325baa2a732b.png)
`View Results Tree`は、JMeterテストの実行結果を詳細に視覚的に表示するリスナー（Listener）です。
サンプラーからの各リクエストと、それに対応するレスポンス情報をツリー形式で表示します。以下の情報が表示されます。

```
・Sampler result : リクエストの詳細情報（例えば、リクエストメソッド、URL、リクエストヘッダー、パラメータなど）のほかに、 応答のステータス（成功または失敗）、応答時間、レスポンスコードなども表示
・Request : 実際に送信されたHTTPリクエストの詳細を表示
・Response data : サーバーからのHTTPレスポンスデータを表示。これは通常、HTML、JSON、XMLなどのレスポンスボディの内容
 ```
 
また、`View Results Tree`はテストが正しく動作しているかを確認する際に特に便利なツールです。
リクエストが正しいか、必要なパラメータが適切に設定されているか、期待したデータがレスポンスとして得られているかなどを確認することができます。
ただし、大量のリクエストを処理する際や高負荷テストを実行する際には、`View Results Tree`は大量のメモリとCPUリソースを消費します。
そのため、実際のロードテストを実行する場合は、このリスナーを無効化することが推奨されます。

:::note info
参考）スクリプトの実行結果の確認：問題あり：赤色、問題なし：緑色
:::

### Aggregate Report
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3647569/152249c9-91b2-3faa-d92b-27e95a337219.png)

`Aggregate Report`は、テスト結果を集約した統計情報を提供するリスナー（Listener）の一つです。
これは複数のリクエストに対する全体的なパフォーマンスメトリクスを提供し、Webサービスのパフォーマンスやシステムのレスポンスタイム、スループットなどを理解するのに役立ちます。
`Aggregate Report`は以下の情報を表示します。

```
・Label：サンプラー名またはリクエスト名
・Number of Samples（# Samples）：そのサンプラーが実行された回数
・Average（Avg）：レスポンスタイムの平均値（ミリ秒）
・Median：レスポンスタイムの中央値（ミリ秒）
・90% Line：90%のリクエストが完了するまでのレスポンスタイム（ミリ秒）
・Min：最小レスポンスタイム（ミリ秒）
・Max：最大レスポンスタイム（ミリ秒）
・Error %：エラーレスポンスの割合
・Throughput：単位時間あたりのリクエスト処理数
```  

このような情報を見ることで、どのリクエストが最も遅いか、エラーが多いか、といった問題を特定することができます。
また、全体的なシステムパフォーマンスを評価し、ボトルネックや高負荷などパフォーマンスに関する問題を識別するのに役立ちます。
ただし、`Aggregate Report`にはリアルタイムの結果を表示する能力が無く、またグラフの表示も提供していません。これらの機能が必要な場合は、他のリスナーを使用します。

# おわりに
JMeterの力を活かし、システムの強度や信頼性を高めることで、ユーザーエクスペリエンスの向上を支えてきました。今後も迅速な変化に対応し、新たな挑戦に立ち向かっていく中で、JMeterは不可欠なツールであり続けます。皆さまのプロジェクトでもJMeterを用いて、成功への一助となることで、技術的リーダーシップを発揮されることを願っています。

※ 記載されている会社名、製品名、サービス名は、各社の商標または登録商標です。