💣

【Artillery】PlaywrightのE2Eテストシナリオで負荷テストする

2024/09/24に公開

はじめに

負荷テストツール「Artillery」を触ってみたのでご紹介します。

ArtilleryにはE2Eテストツール「Playwright」のエンジンが組み込まれており、
Playwrightのテストコードをもとに負荷テストがおこなえます。
E2Eテストをもとにパフォーマンスを検証するため、以下の特徴があります。

  • ユーザーのブラウザ操作と同様の状況で負荷を計測できる。
    • フロントエンドとバックエンドの負荷を、総括してテストできる。
  • テストコードを作成する時間を短縮できる。
    • Playwrightのcodegen機能を使うと、画面操作でコード作成が可能。

https://www.artillery.io/docs/reference/engines/playwright

Current limitations
TypeScript support is available but experimental.

Playwrightのテストコードを利用する上での制限として、
「TypeScriptのサポートは実験段階」とドキュメントに書かれていました🤔
が、実験的にTypeScriptのコードで試してみます!

PlaywrightとArtilleryのインストール

  1. Playwrightをインストールします。
    npm init playwright@latest
  2. Playwrightのテストコードで使う言語などを選択します。
  3. Artilleryをインストールします。
    npm install artillery@latest
  4. 以下コマンドで、Artilleryのインストールを確認します。
    npx artillery dino

    恐竜が表示されました🦕

ArtilleryとPlaywrightの互換性

Load testing with Playwright and Artillery – Artillery Docs

ArtilleryとPlaywrightのバージョンが、乖離しないよう注意が必要です。
npx playwright --versionnpx 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の負荷テストシナリオ用ファイルを作成し編集します。

  1. Artillery用ファイルを格納するフォルダを作成します。
  2. Playwrightのテストコードをコピーして別名で保存します。
    例:artillery/todo.ts
  3. 保存したファイルをエディタで開きます。
  4. 以下編集をおこないます。
    • 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();などを削除

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形式で定義します。

  1. 公式ドキュメントを参考にymlファイルを作成します。
    例:artillery/todo.yml
  2. 負荷テストの実行条件として、以下項目などを記述します。
    • config
      • target:テスト対象のURLを指定する
      • engines:テストエンジンとしてplaywright: {}を指定する
        {}実行オプションを設定可能
      • processor:テストシナリオのファイル名を指定する
      • phases:アクセスする期間やユーザー数などを指定する
    • scenarios
      • engine:シナリオエンジンとしてplaywrightを指定する
      • testFunction:シナリオの実行関数名を指定する

負荷テスト実行条件の定義例

デモ用サイトですが、ご迷惑をかけないようアクセス数は控えめにします🫥

config:
  target: 'https://demo.playwright.dev/todomvc/'
  engines:
    playwright: {}
  processor: 'todo.ts'
  phases:
    # 3秒間に毎秒2ユーザーがアクセスする
    - duration: 3
      arrivalRate: 2
scenarios:
  - engine: playwright
    testFunction: 'addTask'

テスト実行と結果確認

  1. 以下コマンドで負荷テストを実行します。
    npx artillery run {ymlファイルのパス}
  2. 以下のようなレポートが表示されるので、結果を確認します。

レポートは「コアウェブバイタル(Core Web Vitals)」をもとに、ページの読み込み時間などが出力されます。
コアウェブバイタルはGoogle社がユーザー体験(UX)を評価するために定義した指標で、パフォーマンスに関する指標も含まれています。

出力される指標の詳細は、以下ドキュメントに記載されています。

出力される主な指標

メトリック 説明
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など多々ありますが、
それぞれ特徴やメリット/デメリットを把握して活用していければと思います。

コラボスタイル Developers

Discussion