Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

CursorとPlaywright MCP Serverを活用したJMeterの負荷シナリオ生成

導入

こんにちは!名刺アプリ「Eight」のSREエンジニアの藤原です。SREは日々プロダクトの信頼性向上に向き合っています。私は2024年の8月に入社し、すでに入社から1年経ったことに驚きを隠せない今日この頃です。遅ればせながらドラゴンクエストIIIリメイクを始めたので、残暑を乗り越えられるように頑張ります。

アプリケーションへ継続的にリクエストをかける環境を用意するにあたって、JMeterのスクリプトを作成する必要がありました。本ブログでは、CursorとPlaywright MCP Serverを活用してスクリプト作成を効率化した方法をご紹介します。

なお、本記事で触れるJMeterのシナリオの用途としては、いわゆる「ストレステスト」「限界テスト」などの一般的な負荷テストの意味合いではなく、ある程度のユーザーの挙動を模擬しながら継続的にリクエストを流し続けるための用途となります。

背景

Eightでは8月上旬にECS on EC2からFargateへ移行をするプロジェクトを完了させました。移行プロジェクトにおいて、サービスの信頼性を落とさずに移行を完了させることを目指していました。そのためには、本番環境での移行中にリクエストがダウンタイムなしにさばけるか、レスポンスタイムが悪化しないか、エラー率が上昇しないかを計測する必要があります。

ただし、ステージング環境で移行のリハーサルをするにあたって、アプリケーションへ一定の負荷を継続的にかける環境がありませんでした。ステージング環境でレスポンスタイムやエラー率を計測しながら移行リハーサルを実施するために、ユーザーのリクエストを一定模擬したシナリオを作成したかった、というのが動機となります。

アプリケーションに継続的に負荷をかける環境については、今後チーム関係なくエンジニアであれば活用できる基盤を用意しておきたかったため、AWSソリューションであるDistributed Load Testing on AWSを採用しました。単一のHTTPエンドポイントへのリクエストだけでなく、JMeter スクリプトもサポートされています。なお、本題ではないので詳細は触れませんが、アクセス経路を限定したりアウトバウンド通信はNAT Gatewayを通すように変更したりなど、一部カスタマイズをしています。

課題

上記で触れたようにDistributed Load Testing on AWSを採用しているため、ユーザージャーニーを模擬するにはJMeterでのシナリオを作成する必要がありました。恥ずかしながら私自身はJMeterに詳しくなく、認証後のヘッダーの設定や各ページを訪れた際にバックエンドで叩かれる複数のAPIリクエストを再現してシナリオを作成していくのが骨の折れる作業でした。

JMeterにはHTTP プロキシサーバーという、ブラウザ操作を記録してシナリオを作成してくれる機能があります。しかし、外部リソースへのリクエストや静的アセットの取得まで記録されてしまうため、必要なリクエスト以外は削除していく必要がありそうでした。また、ブラウザの操作自体は人間が行う必要もあり、学習コストを抑えながら効率よく、どのようにシナリオを作成していけば良いかを考えていました。

そんな中、AWS Summit Japan 2025に参加し、「生成 AI 時代の負荷テスト on AWS 〜 AI は負荷テストをどれだけ爆速にできるか?〜」のセッションを聴講しました。私たちが実施しようとしているのはセッションで紹介されているような本格的な負荷テストではありませんが、JMeterでのシナリオを作成するにあたって参考にできる部分が多いと考えました。ちなみにセッションではk6、Amazon Q Developer CLIを活用されていました。

手法

エディタ兼AIエージェントのクライアントとしてCursor、モデルはClaude Sonnet 4、ユーザージャーニーを模擬するツールとしてPlaywright MCP Serverを活用しました。クライアントに関してはお好みのクライアントで問題ないと思います。

スクリプトを作成するプロジェクト構成はAWS Summitのセッションも参考にし、次のような形としました。

project_root_dir/                              
├── .cursor/                            
│   └── rules/                          
│       ├── project-rule.mdc            # プロジェクト全体ルール
│       ├── jmeter-rule.mdc             # JMeter作成方針ルール
│       ├── Architecture/               # アーキテクチャドキュメント
│       │   ├── system.mdc              # アプリケーションのシステム設計
│       │   └── infrastructure.mdc      # インフラ構成・負荷テスト判定基準
│       └── playwriter/                 
│           ├── user-journey.mdc        # 実施対象のシナリオ
│           └── playwriter-profile.mdc  # Playwright操作・キャプチャルール
├── JMeter/                             
│   ├── eight_load_staging_scenario.jmx # 生成されるJMeterスクリプト
│   └── Sample/                         
│       └── test.jmx                    # 正常に動くサンプルスクリプト
└── work/                               
    ├── S001_login.md                   # Playwrightによって生成されるログインシナリオのネットワークキャプチャ
    ├── S002_huga.md                    # Playwrightによって生成されるhugaのネットワークキャプチャ
    └── S003_hoge.md                    # Playwrightによって生成されるhogeのネットワークキャプチャ

.cursor/rulesに各ルールファイルを用意し、入力プロンプトを投げることでworkディレクトリ配下にシナリオごとのネットワークキャプチャが記録されていき、それらを基に最終的にはJMeterディレクトリ配下にJMeterスクリプトが生成される、といった流れです。

本ブログで出てくるシナリオとは、例えば、Eightでは認証 ⇒ トップページ遷移 ⇒ フィード投稿の確認 ⇒ プロフィール情報の更新 ⇒ お知らせの確認など、ユーザージャーニーを分解したものを指しています。

プロジェクトの全体構造だけではよくわからないと思うので、各ファイルの中身を一部抜粋してご紹介します。

[project-rule.mdc]

プロジェクト全体の司令塔のような役割を担っています。

# 負荷テストシナリオ作成プロジェクト

## プロジェクトの目的

本プロジェクトは、**負荷テストのシナリオを作成し、最終的にJMeterの形式で出力する**プロジェクトです。
ユーザーが操作する内容をPlaywright MCPで模擬し、ネットワークキャプチャを作成した上でJMeterで使える形に変換します。つまり、開発ワークフローの2と3をCursor AIが実施します。

### 主な目標
- リアルなユーザー操作の模擬
- 負荷テストシナリオの作成
- ...

## プロジェクト構造
<上記で紹介した構造を記載>

## プロジェクトロール定義

### 1. PlayWriter Role
- **責任**: Playwright MCPを使用したブラウザ操作のシミュレーション
- **成果物**: ネットワークキャプチャ(`work/*.md`- **参照**: `rules/playwriter/playwriter-profile.mdc``rules/playwriter/user-journey.mdc`

### 2. JMeter Role  
- **責任**: ネットワークキャプチャからJMeterテストプランの生成
- **成果物**: JMeterテストプラン(`JMeter/*.jmx`- **参照**: `rules/jmeter-rule.mdc``rules/Architecture/infrastructure.mdc``rules/Architecture/system.mdc`

## 開発ワークフロー

### 1. 要件定義フェーズ
1. **プロジェクトルールに実施対象シナリオを記載**

### 2. キャプチャフェーズ(PlayWriter Role)
1. **Playwright MCP でブラウザを立ち上げ**
2. **`rules/playwriter/user-journey.mdc`に記載されたシナリオをシミュレーション**
3. **全ステップ完了後、ネットワークキャプチャをマークダウンで出力**
4. `work/network_capture.md` への保存

### 3. 変換フェーズ(JMeter Role)
1. `rules/Architecture/infrastructure.mdc` の判定基準を適用
2. **ネットワークキャプチャから負荷テスト対象リクエストの選別**
3. **JMeterテストプラン(.jmx)の生成**
4. `JMeter/` ディレクトリへの保存

## その他補足

~~ 略 ~~

[jmeter-rule.mdc]

JMeterのシナリオを作成する際に参照されるルールです。使用しているJMeter のバージョンで則ってほしい構文ルールを可能な限り細かく記載しています。

# JMeter Role

あなたは、Playwrightによって生成されたネットワークキャプチャを元にJMeterのシナリオを作成するエキスパートです。

## Architecture参照

JMeterシナリオ作成時は、**必ず `.cursor/rules/Architecture/infrastructure.mdc`と `.cursor/rules/Architecture/system.mdc` を参照**してください。
このファイルには以下の重要な情報が含まれています:

- 負荷テスト対象の判定基準
- 含めるべきリクエストと除外すべきリクエストの詳細
- インフラストラクチャとアプリケーションの構成

## タスク

workディレクトリに存在するネットワークキャプチャを読み取り、**infrastructure.mdcの判定基準に基づいて**以下に従って`.jmx`ファイルを作成してください。

### JMeter設定要件
- JMeterのバージョンは×.×.×互換で作成してください
- ファイルはJMeterフォルダに保存してください
- 変数でBASE_URLを使う場合は「ドメイン名のみ」、Protocolは「https」とし、Server Name or IPに「https://」や「/」は入れてはいけません
- ...
- ユーザー数はN人と仮定してください
- リクエスト数はNで作成してください
- Concurrency数はNで作成してください
- ...

## 重要: JMeter ×.×.× 正しい構文ルール

**以下の構文ルールを厳密に守ってください。これらに従わないとJMeterでエラーが発生します。**

### ログインページでのAuthenticity Token取得
```xml
<HTTPSamplerProxy testname="ログインページアクセス">
...
```

~~ 略 ~~ 

[system.mdc]

モバイルやWebのフロントエンド、バックエンドの構成を記載しています。

# Eight System Patterns

## システムアーキテクチャ
Eightのサービスは以下のような構成で成り立っています。
### バックエンド
~~ 略 ~~ 

[infrastructure.mdc]

インフラストラクチャの構成と負荷シナリオの対象、何をテストすべきかの判定基準を記載しています。負荷テスト除外対象も記載することで、無駄なリクエストをシナリオから排除するようにしています。

# Eightインフラストラクチャガイド

## 構成概要
Eightのインフラストラクチャは、huga を採用しています。

### 主要コンポーネント
1. **CDN・WAF層**: hoge
2. **静的アセット**: hogehoge
3. ...
~~ 略 ~~

## 負荷テスト対象の判定基準
### 負荷テスト対象
...
### 負荷テスト除外対象(静的アセット・外部サービス)
...
~~ 略 ~~

[user-journey.mdc]

実際に実行してほしいシナリオを記載しています。Playwrightが再現可能な形でパスやどこを操作すれば良いかまで記載している点がポイントです。

# Eight ユーザージャーニー定義

## 概要

本ファイルは、Eightにおける負荷シナリオ用のユーザージャーニーを定義します。
Playwright MCPを使用してこれらのシナリオを実行し、それぞれのシナリオに対してネットワークキャプチャを生成します。

## 📋 実施対象シナリオ
- **対象URL**: https://<Your Domain>

### S001: ログインページアクセス
- **シナリオ名**: ログインページ閲覧(ログイン処理は初回のみ実行)
- **操作フロー**:
  1. ブラウザでログアウトページへ遷移(/<Path to logout>)し、ログアウトする
  2. ログインページ(/<Path to login>)へ遷移
  3. ...
~~ 略 ~~

[playwriter-profile.mdc]

Playwright実行時の品質基準を定めています。

# PlayWriter Role

あなたは、Playwrightを使ってユーザーのWebサイト操作をシミュレーションするエキスパートです。
今回のプロジェクトの目的となる負荷テストのシナリオを作成するため、提示されたWebサイトの操作シナリオをMCPのPlaywrightを使ってシミュレーションし、各シナリオごとにネットワークキャプチャを保存してください。1つのファイルにまとめてはいけません。

## 役割と責任

### 主要責任
1. **ユーザージャーニーの実装**: `user-journey.mdc` で定義されたシナリオの正確な実行
2. **ネットワークキャプチャ**: 全てのHTTPリクエスト/レスポンスの詳細記録

### 参照ドキュメント
- **必須参照**: `user-journey.mdc` - 実行すべきシナリオ定義
- **プロジェクト方針**: `../project-rule.mdc` - 全体ガイドライン

## 実行タスク

### 基本フロー
1. **Playwright MCP でブラウザを立ち上げ**
2. **user-journey.mdc に記載されたシナリオを段階的にシミュレーション**
~~ 略 ~~

上記ファイルが用意できたら、いざスクリプトを生成してもらいます。入力プロンプトは非常にシンプルです。

入力プロンプト:

負荷テストのシナリオを作成し、JMeterの形式で出力してください

出力プロンプト (一部簡略化、マスキングしています):

負荷テストのシナリオ作成をお手伝いします。まず、プロジェクトの構成と参照すべき設計ドキュメントを確認させていただきます。

~~ 略 ~~

設計ドキュメントを確認しました。Nつのシナリオ(S001〜S00N)が定義されており、Eight(<URL>)に対する負荷テストシナリオを作成します。
まずはPlaywriter Roleとして、Playwright MCPでユーザージャーニーをシミュレーションして、各シナリオのネットワークキャプチャを生成いたします。

~~ (略)MCP Serverにアクセスして各種ツールが実行されていく ~~

S001: ログインページアクセス を実行します。まずはログアウトから開始します。

~~ (略)MCP Serverにアクセスしてシナリオ内のアクションが自動で実行されていく ~~

ログインが成功し、hugahugaページにリダイレクトされました。S001のシナリオが完了しました。
次に、S001シナリオのネットワークキャプチャを記録します。

~~ (略)MCP Serverにアクセスしてネットワークキャプチャが取得され、workディレクトリに保存される ~~

S001のネットワークキャプチャファイルが作成されました。次にS002のシナリオ「huga」を実行します。

~~ (略)全てのシナリオに対して自律的にネットワークキャプチャが取得されていく ~~

次のステップとして、JMeter Roleに切り替えて、作成されたネットワークキャプチャを基にJMeterテストプラン(.jmx)を生成します。
まず、設計ドキュメントを確認してJMeterファイルを作成します。

次に、ネットワークキャプチャを基にJMeterテストプランを作成します。infrastructure.mdcの判定基準に従って、負荷テスト対象リクエストを選別します。

ネットワークキャプチャを基に、JMeter 5.6.3互換のテストプランを作成します。infrastructure.mdcの判定基準に従って、負荷テスト対象リクエストのみを含めます。

🎉 負荷テストシナリオ作成完了!

~~ 略 ~~

イメージをしやすいように処理中の画像も載せます。

Cursorによる処理の一例

今回のケースでは10分程度でスクリプトが完成しました。お茶やコーヒーを飲んでゆっくり待ちましょう。実行が完了するとJMeterディレクトリに.jmxファイルが生成されるので、JMeterを立ち上げてスクリプトが正常に動作することを確認します。確認が取れたらDistributed Load Testing on AWSにアップロードして負荷シナリオを開始するだけです。

もちろん1回で完璧なスクリプトが生成されることもなく、作成されたスクリプトをJMeterで流してみると、一部のシナリオに含まれるAPI呼び出しでエラーになることもありました。特に苦労したポイントと解決方針をご紹介します。

例えば、あるシナリオの連絡先数をカウントするAPIや名刺検索APIで404エラーが頻発しました。Railsアプリケーションでは、CSRF保護のためにauthenticity_tokenパラメータが必要になりますが、生成されたシナリオ内で呼び出すAPIにはこのパラメータが付与されていませんでした。入力プロンプトで指示を出しながら修正されることが確認できたら、jmeter-rule.mdcにルールとして追記します。

また、私が事前に作成して動くことが確認できたスクリプトをSampleディレクトリに配置し、jmeter-rule.mdcSampleディレクトリも参考にするようにルールを追記しました。可能な限りプロンプトをシンプルにしてやりとりを少なくさせる、かつ、使う人によらず出力結果を安定させるために、プロンプトを充実させるのではなくルールに追記していく方針をとっています。もちろんルールの変更もAIに任せることも可能です。ただし、追記しなくても良いルールを追記したり、AIらしさ全開の絵文字を追加したりするのですべてを任せるのは要注意です。

このようにすることで、自律的に次のような修正をしてくれるようになりました。

JMeterテストプランで一部のHeaderManagerに誤りがあったので修正します。

学び

環境のセットアップと試行錯誤を含めても、約1日で最低限のユーザージャーニーを模擬した負荷シナリオを作成できました!今後シナリオを拡充させていく際でも、数日かかるような作業が数時間、もしくは1時間未満で完了することが期待できます。もちろん、うまくいかないこともあると思うので、AIの進化とともに手法やプロジェクト設計も見直していきます。他でも似たような事例やうまくできた事例があれば取り入れていきたいです。

ルールはAIの振る舞いを一貫して制御するためのガイドラインであるという点を意識して、プロンプトを充実させるのではなく、従ってほしいことをルールに寄せていくことで、活用する人によらず出力を安定化できるというのも学びでした。インタラクティブに指示できるプロンプトで頑張りがちですが、毎回同じ指示で修正をしてもらうような場合はルールをうまく活用できると良さそうです。

今後・まとめ

まだまだ試行錯誤段階ですが、負荷シナリオのスクリプト生成だけでなく、QAテストのシナリオ作成でも幅が広がりそうな活用方法だと感じました。Eightのヘルプページにはキャプチャ付きで操作方法が記載されているため、user-journey.mdcの代替になり得そうです。こちらは次のステップとして試していこうと思っています。
eight.zendesk.com

外部のイベントをきっかけに知見を吸収して業務に生かすことができたため、外の世界を知るというのは大事だと改めて感じました。そして本記事が負荷シナリオのスクリプト生成を効率化したい方、生成AIやMCPサーバーの活用方法を少しでも知りたい方の参考になれば幸いです。

最後に、Eightでは一緒に働いてくださるエンジニアを募集中です。
専門性を深めながらも領域を超えた成長にチャレンジしたい方、SREとしてアプリケーション領域にも興味を持っている方、または開発者としてインフラにも関わりたい方、ぜひ一緒にEightを作り上げていきましょう!
media.sansan-engineering.com

© Sansan, Inc.