Playwrightを使用したHTTPリクエストのテスト

## 1. はじめに

#### HTTPリクエストのテスト

ウェブアプリケーションでは、ユーザー操作や自動処理に応じてバックエンドにHTTPリクエストを送信する処理を実装することがあります。ウェブアプリケーションのテストを行う上で、これらのリクエストが意図した通りに送信されているかを確認することは重要です。

この記事では、E2EテストツールであるPlaywrightを使って、フロントエンドから送信するHTTPリクエストのパスやクエリパラメータをテストする方法を紹介します。

#### Playwrightとは

Playwrightは、Microsoftが開発したオープンソースのE2Eのテスト自動化ツールです。主にウェブアプリケーションのテストを自動化するために使用され、ブラウザ上で実行されるユーザーインターフェースの動作をエミュレートすることができます。
クロスブラウザ対応で主要なブラウザはサポートしており、シンプルなAPIを利用して直感的にコードを記述することができます。

#### PlaywriteによるHTTPリクエストのテスト

Playwrightは、ブラウザ上で発生するイベントを監視する機能を提供しており、`page.on`メソッドを使ってリクエストを監視することができます。
これにより、ユーザー操作やページ遷移によって送信されるHTTPリクエストをリアルタイムにキャプチャし、リクエストのパスやクエリパラメータを確認することが可能です。

以下では、Playwrightを使ってHTTPリクエストのテストを行う方法を具体的なコード例とともに解説します。

## 2. HTTPリクエストを取得

#### 基本的な使用例

以下は、`page.on('request')`を使ってリクエストのURLやクエリパラメータをコンソールに出力する例です。

```typescript
const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  // リクエストイベントを監視
  page.on('request', (request) => {
    const url = new URL(request.url());
    console.log(`Request URL: ${url.pathname}`);
    console.log(`Query Params: ${url.searchParams}`);
  });

  // テスト対象ページにアクセス
  await page.goto('https://example.com/search');

  // フォームにデータを入力してリクエストを送信
  await page.fill('input[name="search"]', 'playwright');
  await page.click('button[type="submit"]');

  await browser.close();
})();
```

#### コード解説

1. `chromium`, `browser`, `page`:
  - `chromium` : 
    Google Chromeの基盤であるChromiumブラウザを操作するためのPlaywrightモジュールです。chromium.launch() を呼び出すことでブラウザが起動します
  - `browser` : 起動中のChromiumインスタンス全体を制御するオブジェクトです。browser.newPage() を使って、新しいタブ（ページ）を作成して操作できます
  - `page` : browser.newPage() で生成されたオブジェクトで、ブラウザのタブやページそのものを操作します。ページの移動や要素の操作、イベントの監視を行うためのメソッドが含まれています

2. `page.on('request', callback)`: 
  ブラウザからのリクエストが発生するたびに、指定されたコールバック関数が実行されます。このコールバック内でリクエストのURLを`request.url()`を使って取得し、URLオブジェクトにパースしています
3. `new URL(request.url())`:
  URLをオブジェクトに変換することで、`url.pathname`でパスを、`url.searchParams`でクエリパラメータを簡単に確認できます


## 3. 実践的なテストケース

#### 例1: 検索機能のリクエスト検証

ユーザーが検索ボックスにキーワードを入力し、検索ボタンをクリックした際に、正しいクエリパラメータを持ったリクエストがサーバーに送信されているかを検証します。

```typescript
test('検索機能のリクエスト検証', async ({ page }) => {
    const expectedKeyword = 'playwright';

    page.on('request', (request) => {
        const url = new URL(request.url());
        if (url.pathname === '/api/search') {
            const actualKeyword = url.searchParams.get('keyword');

            // keyword キーが存在するか確認
            expect(actualKeyword).toBeDefined();

            // キーワードの値を比較
            expect(actualKeyword).toBe(expectedKeyword);
        }
    });

    // テスト対象のページに移動
    await page.goto('https://example.com/search');

    // フォームにデータを入力してリクエストを送信
    await page.fill('[data-testid="keyword-input"]', 'playwright');
    await page.click('[data-testid="submit-button"]');
});

```

#### 例2: APIエンドポイントへの複数リクエストの検証

ページのロード時やユーザー操作によって、複数のAPIエンドポイントにリクエストが発生する場合、それぞれのリクエストが正しいかを検証します。

```typescript
test('APIエンドポイントへの複数リクエストの検証', async ({ page }) => {
    let userApiCalled = false;
    let productsApiCalled = false;

    page.on('request', (request) => {
        const url = new URL(request.url());
        if (url.pathname === '/api/user') {
            // クエリパラメータの検証
            expect(url.searchParams.get('id')).toBe('123');
            // リクエストメソッドの検証
            expect(request.method()).toBe('GET');

            userApiCalled = true; // フラグを設定

        } else if (url.pathname === '/api/products') {
            // クエリパラメータの検証
            expect(url.searchParams.get('category')).toBe('electronics');
           // リクエストメソッドの検証
            expect(request.method()).toBe('POST');

            productsApiCalled = true; // フラグを設定
        }
    });

    await page.goto('https://example.com');
    expect(userApiCalled).toBe(true); // /api/user へのリクエストが発生したか検証
    expect(productsApiCalled).toBe(true); // /api/products へのリクエストが発生したか検証
});
```

#### 例3: フォーム送信のリクエスト検証

ユーザーがフォームを送信した際に、POSTリクエストが正しく送信されているか、リクエストボディやパラメータが正確であるかを確認します。

```typescript
test('フォーム送信のリクエスト検証', async ({ page }) => {
    // リクエストイベントのリスナーを設定
    page.on('request', (request) => {
        if (request.method() === 'POST' && request.url().includes('/submit-form')) {
            const postData = request.postData();

            // フォームデータが存在することを確認
            expect(postData).not.toBeNull();

            // フォームデータをURLSearchParamsオブジェクトとしてパース
            const params = new URLSearchParams(postData);

            // 特定のキーとその値を検証
            expect(params.has('field')).toBe(true); // 'field' というキーが存在するか
            expect(params.get('field')).toBe('playwright'); // 'field' の値が 'playwright' であるか
        }
    });

    // テスト対象のページに移動
    await page.goto('https://example.com');

    // フォームにデータを入力してリクエストを送信
    await page.fill('[data-testid="field-input"]', 'playwright');
    await page.click('[data-testid="submit-button"]');
});
```

## 4. まとめ

この記事では、Playwrightを使ってHTTPリクエストのテストを行う方法について紹介しました。Playwrightの`page.on`メソッドを使うことで、ブラウザ上で発生するリクエストを監視し、リクエストのパスやクエリパラメータを取得することができます。

以下は、HTTPリクエストのテストにおいて役立つポイントです。

- Playwrightを使ってHTTPリクエストをテストする際には、`page.on`メソッドを使ってリクエストを監視する
- リクエストのURLを取得し、URLオブジェクトに変換することで、パスやクエリパラメータを取得できる
- リクエストのパスやパラメータを検証することで、APIエンドポイントの正確性やデータの送信を確認できる

Playwrightを使ってHTTPリクエストのテストを行うことで、ウェブアプリケーションの正確性や安全性を確保することができます。ぜひ、実際のテストコードに取り入れてみてください。

## 5. 参考リンク

- [Playrgiht公式ドキュメント](https://playwright.dev/docs/api/class-page#page-event-request)
- [Playwrightでリクエストを確認する](https://zenn.dev/sdb_blog/articles/002-check_post_request_by_playwright)