【Artillery】PlaywrightのE2Eテストシナリオで負荷テストする
はじめに
負荷テストツール「Artillery」を触ってみたのでご紹介します。
ArtilleryにはE2Eテストツール「Playwright」のエンジンが組み込まれており、
Playwrightのテストコードをもとに負荷テストがおこなえます。
E2Eテストをもとにパフォーマンスを検証するため、以下の特徴があります。
- ユーザーのブラウザ操作と同様の状況で負荷を計測できる。
- フロントエンドとバックエンドの負荷を、総括してテストできる。
- テストコードを作成する時間を短縮できる。
- Playwrightの
codegen
機能を使うと、画面操作でコード作成が可能。
- Playwrightの
Current limitations
TypeScript support is available but experimental.
Playwrightのテストコードを利用する上での制限として、
「TypeScriptのサポートは実験段階」とドキュメントに書かれていました🤔
が、実験的にTypeScriptのコードで試してみます!
PlaywrightとArtilleryのインストール
-
Playwrightをインストールします。
npm init playwright@latest
- Playwrightのテストコードで使う言語などを選択します。
-
Artilleryをインストールします。
npm install artillery@latest
- 以下コマンドで、Artilleryのインストールを確認します。
npx artillery dino
恐竜が表示されました🦕
ArtilleryとPlaywrightの互換性
Load testing with Playwright and Artillery – Artillery Docs
ArtilleryとPlaywrightのバージョンが、乖離しないよう注意が必要です。
npx playwright --version
とnpx artillery version
でバージョンを確認しましょう。
因みに今回試したのは、以下バージョンでした。
- Artillery:2.0.20
- Playwright:1.47.1
テストコードを作る
【1】PlaywrightでE2Eテストを作成する
今回は、以前codegen
を使いノーコードで作成したテストコードを流用します。
Playwrightのテストコード作成手順は、以下記事などを参考にしてください。
Playwrightのテストコード例
デモ用ToDo管理アプリにアクセス後、タスクを2つ登録し、1つのタスクを完了にしています。
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc/#/');
await page.getByPlaceholder('What needs to be done?').fill('task A');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await expect(page.getByTestId('todo-title')).toBeVisible();
await page.getByPlaceholder('What needs to be done?').click();
await page.getByPlaceholder('What needs to be done?').fill('task B');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await expect(page.locator('body')).toContainText('task B');
await page.locator('li').filter({ hasText: 'task A' }).getByLabel('Toggle Todo').check();
await expect(page.locator('li').filter({ hasText: 'task A' }).getByLabel('Toggle Todo')).toBeChecked();
});
【2】負荷テスト用のテストシナリオを作成する
Playwrightのテストコードをもとに、
Artilleryの負荷テストシナリオ用ファイルを作成し編集します。
- Artillery用ファイルを格納するフォルダを作成します。
- Playwrightのテストコードをコピーして別名で保存します。
例:artillery/todo.ts
- 保存したファイルをエディタで開きます。
- 以下編集をおこないます。
- Playwrightのライブラリimport処理を追加する【必須】
import { Page } from 'playwright';
を追加 - テストする画面操作の処理を関数化する【必須】
test('test', async ({ page }) => { ... });
↓
export async function addTask(page: Page) { ... }
- テスト対象のURLを相対パスにする(ベースURLをArtilleryで定義するため)
page.goto('https://demo.playwright.dev/todomvc/#/');
↓
page.goto('/#/');
- E2E用入力チェック処理(アサーション)を削除する
await expect(page.getByTestId('todo-title')).toBeVisible();
などを削除
- Playwrightのライブラリimport処理を追加する【必須】
Artilleryのテストコード例
タスクを2つ登録し、1つのタスクを完了にする処理は、Playwrightのコードのままです。
import { Page } from 'playwright';
export async function addTask(page: Page) {
await page.goto('./#/');
await page.getByPlaceholder('What needs to be done?').fill('task A');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await page.getByPlaceholder('What needs to be done?').click();
await page.getByPlaceholder('What needs to be done?').fill('task B');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await page.locator('li').filter({ hasText: 'task A' }).getByLabel('Toggle Todo').check();
}
【3】負荷テストの実行条件を定義する
負荷テストの実行条件を、YAML形式で定義します。
-
公式ドキュメントを参考にymlファイルを作成します。
例:artillery/todo.yml
- 負荷テストの実行条件として、以下項目などを記述します。
- config
- target:テスト対象のURLを指定する
- engines:テストエンジンとして
playwright: {}
を指定する
※{}
で実行オプションを設定可能 - processor:テストシナリオのファイル名を指定する
- phases:アクセスする期間やユーザー数などを指定する
- scenarios
- engine:シナリオエンジンとして
playwright
を指定する - testFunction:シナリオの実行関数名を指定する
- engine:シナリオエンジンとして
- config
負荷テスト実行条件の定義例
デモ用サイトですが、ご迷惑をかけないようアクセス数は控えめにします🫥
config:
target: 'https://demo.playwright.dev/todomvc/'
engines:
playwright: {}
processor: 'todo.ts'
phases:
# 3秒間に毎秒2ユーザーがアクセスする
- duration: 3
arrivalRate: 2
scenarios:
- engine: playwright
testFunction: 'addTask'
テスト実行と結果確認
- 以下コマンドで負荷テストを実行します。
npx artillery run {ymlファイルのパス}
- 以下のようなレポートが表示されるので、結果を確認します。
レポートは「コアウェブバイタル(Core Web Vitals)」をもとに、ページの読み込み時間などが出力されます。
コアウェブバイタルはGoogle社がユーザー体験(UX)を評価するために定義した指標で、パフォーマンスに関する指標も含まれています。
出力される指標の詳細は、以下ドキュメントに記載されています。
- ユーザー中心のパフォーマンス指標 | Articles | web.dev
- Load testing with Playwright and Artillery – Artillery Docs
- Metrics – Artillery Docs
出力される主な指標
メトリック | 説明 |
---|---|
vusers.created | 作成した仮想ユーザーの数。 |
vusers.completed | 完了した仮想ユーザーの数。 |
vusers.failed | 失敗した仮想ユーザーの数。 |
vusers.session_length | 仮想ユーザーが各セッションを完了するまでの時間(ミリ秒)。 min(最小値)、max(最大値)、mean(平均値)、median(中央値)、p95(95パーセンタイル)、p99(99パーセンタイル) |
browser.http_requests | 実行したHTTPリクエストの数。 |
browser.page.FCP | 最初のコンテンツが画面に表示されるまでの時間(ミリ秒)。 FCP:First Contentful Paint |
browser.page.FID | クリックなど最初のユーザー操作に対するブラウザの応答時間(ミリ秒)。 FID:First Input Delay |
browser.page.LCP | 最も大きな画像またはテキストブロックが表示されるまでの時間(ミリ秒)。 LCP:Largest Contentful Paint |
browser.page.TTFB | 最初のレスポンスがブラウザに到着するまでの時間(ミリ秒)。 TTFB:Time To First Byte |
おわりに
「Artillery」は画像表示やJavaScriptのフロントエンドから、
API実行のバックエンドまでユーザー視点で総括してパフォーマンスを計測でき、
Playwrightのテストシナリオ活用で簡単に負荷テストを実行できて便利でした。
深く試せれていないですが、TypeScriptでも問題は発生しませんでした。
複数シナリオの組み合わせや、段階的なアクセス数増加など、YAML定義で柔軟に変更できます。
また、Artillery Cloudのサービスを利用すると、実行結果をグラフィカルに確認できます。
引用:Getting started with Artillery Cloud
負荷テストツールにはJMeter、k6、Gatling、Locustなど多々ありますが、
それぞれ特徴やメリット/デメリットを把握して活用していければと思います。
Discussion