PlaywrightでWebSocketの通信をモックする

はじめに

Playwrightでは、v1.48.0からWebSocket routingという機能を使ってWebSocket通信をモックしたり通信内容の一部を改変できるようになりました。
動作を試すために実際にサンプルを作ってみたので、サンプルを元にWebSocket routingの機能を紹介します。

環境

  • Playwright v1.48.0
  • Node.js v20.10.0
  • macOS Sonoma 14.6.1

実装

WebSocketのエコーサーバー( wss://echo.websocket.org )に対してメッセージを送り、受け取った内容を画面に表示するだけのシンプルなWebアプリを対象として、Playwrightを使ってテストをしてみます。
今回は、以下のようなテストコードを書いてみました。

import { test, expect } from '@playwright/test';
import { randomUUID } from 'crypto';

test('send message', async ({ page }) => {
  const uuid = randomUUID();
  await page.routeWebSocket(/.*/, (ws) => {
    ws.onMessage((message) => {
      console.log('>', message);
      if (message === uuid) {
        console.log('<', 'Hello, World!');
        ws.send('Hello, World!');
      }
    });
  });

  await page.goto('http://localhost:5173/client/index.html');

  await page.getByLabel('input').fill(uuid);

  await page.getByText('Send').click();

  await expect(page.getByText('Hello, World!').isVisible()).resolves.toBe(true);
});

テストごとにUUIDを生成し、WebSocketのモックにおいてそのUUIDを受信したら Hello, World! というメッセージを返します。
Webアプリでは受信したメッセージをそのまま画面上に出力するため、Hello, World! という文字列が画面上に表示されたらテストが通ります。

モックする対象のWebSocket通信は、 page.routeWebSocket() の引数で設定します。
page.routeWebSocket() の引数には文字列以外にも正規表現を指定することができるので、今回のように全てのWebSocket通信を対象としてモックすることもできます。

WebSocket通信をモックして丸ごと置き換えるのではなく実際のWebSocket通信をさせつつ一部を改変したり読み取る場合は、以下の抜粋したコードように webSocketRoute.connectToServer() を呼び出してサーバー側のWebSocketRouteインスタンスを取得する必要があります。
サーバー側のWebSocketRouteインスタンスWebSocketRoute.send() を呼ぶことで、実物のサーバーにメッセージを転送できます。

await page.routeWebSocket(/.*/, (ws) => {
  const server = ws.connectToServer();
  ws.onMessage((message) => {
    console.log('>', message);
    if (message === uuid) {
      console.log('<', 'Hello, World!');
      ws.send('Hello, World!'); // Playwright側でモックする
    } else {
      server.send(message); // 実物のサーバーにメッセージを送る
    }
  });
});

今回利用したサンプルコードは、GitHubの以下のリポジトリに置いてあります。

github.com

おわりに

これまでは、WebSocketの通信をモックする際は実際にモックサーバーを建てるなどの方法を使っていましたが、アプリケーションを書き換えたりすることなくPlaywrightだけでWebSocket通信のモックができるのはとても便利だなと感じました。
特に、テストコード内でモックの内容を動的に変更してテストしたい場合は、同じコード内でモックの処理を書けるので、かなり可読性が高くなると思いました。
WebSocket通信を行うWebアプリのE2Eテストなどを実装する際は、積極的に使っていこうと思います。