0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PlaywrightでWOFF SDKをモックしながら開発する

0
Last updated at Posted at 2025-12-19

はじめに

WOFF(WORKS Frontend Framework)は、LINE WORKSのフロントエンド開発で使用されるSDKです。 
WOFF SDKを組み込んだアプリケーションをPlaywrightでテストする際、実際のWOFF環境に依存せずテストを実行したい場合があります。
本稿では、Playwrightのpage.routeメソッドを使用してWOFF SDKを力技でモックする方法を解説します。

1. scriptタグで読み込まれるSDKのモック

WOFF SDKは通常、以下のようにscriptタグでHTMLに読み込まれます (Ref. WOFF SDKの指定)

<script charset="utf-8" src="https://static.worksmobile.net/static/wm/woff/edge/3.7.1/sdk.js"></script>

このような外部スクリプトで読み込まれるSDKをテスト時にモックするには、
Playwrightのpage.routeメソッドを使用してスクリプトの読み込みを傍受し、モックされたオブジェクトを返す方法が有効です。

2. page.routeを使ったモック関数の実装

WOFF SDK内で定義されるメソッドに対して、
WOFF SDKドキュメントを参考にモック関数を格納したオブジェクトを取得できるように、以下のような関数を定義します。
引数ではPlaywrightのPageオブジェクトと、上書きのためのmockedObjectsを受け取っています。
これにより、テスト内で個別にモックを上書きして状況を適切に変更できるようにします。
今回のダミーデータの生成にはfaker-js/faker を利用しました。

export const setMockedWoffSdk = async (
  page: Page,
  mockedObjects?: Partial<MockedWoffSdk>,
) => {
  // ダミーデータを生成
  const fakeClientId = faker.string.alphanumeric(10);
  const fakeDomainId = faker.string.alphanumeric(10);
  const fakeDisplayName = faker.string.alphanumeric(10);
  const fakeUserName = faker.internet.userName();
  const mockedWoffSdk = {
    // 認証関連
    isLoggedIn: () => true,
    
    // コンテキスト情報
    getContext: () => ({
      viewType: "full",
      endpointUrl: "http://example.com/my-app/",
      clientId: fakeClientId,
      clientType: "PC_WEB",
    }),
    
    // ユーザー情報
    getProfile: async () => ({
      domainId: fakeDomainId,
      userId: fakeUserName,
      displayName: fakeDisplayName,
    }),
    
    // その他のメソッド
    getLanguage: () => "ja",
    getOS: () => "web",
    init: async (config: { woffId: string }, successCallback: Function) => {
      successCallback();
    },
    // ... その他必要なメソッド ...
    
    // カスタムモックで上書き可能
    ...(mockedObjects ?? {}),
  };


  // 実際のSDKの読み込みを傍受してモックに置き換える
  await page.route("WOFF_SDK_URL", (route) => {
    route.fulfill({
      status: 200,
      contentType: "text/javascript",
      // モックオブジェクトで使用する定数を定義し、window.woffに設定
      body: `
        const fakeClientId = \`${fakeClientId}\`;
        const fakeDomainId = \`${fakeDomainId}\`;
        const fakeDisplayName = \`${fakeDisplayName}\`;
        const fakeUserName = \`${fakeUserName}\`;

        window.woff = ${objToString(mockedWoffSdk)};
      `,
    });
  });
};

3. 実装の仕組み

3.1 page.routeによるスクリプトのモック

await page.route(`${WOFF_SDK_URL}`, (route) => {
  route.fulfill({
    status: 200,
    contentType: "text/javascript",
    body: `/* モックされたJavaScriptコード */`,
  });
});

page.routeメソッドを使用して、WOFF SDKのスクリプトURLへのリクエストを傍受します。 実際のSDKをロードする代わりに、モックされたwindow.woffオブジェクトを含むJavaScriptコードを返します。

3.2 オブジェクトの文字列化

route.fulfillbodyパラメータには文字列を渡す必要があります。そのため、objToStringヘルパー関数を使用して、JavaScriptオブジェクトを実行可能なコード文字列に変換しています。

const objToString = (obj: Object): string => {
  const entries = Object.entries(obj).map(([key, value]) => {
    if (typeof value === "object") {
      return `${key}: ${objToString(value)}`; // ネストしたオブジェクトは再帰的に処理
    }
    return typeof value === "function"
      ? `${key}: ${value.toString()}`  // 関数は文字列化
      : `${key}: ${value}`;              // プリミティブ値はそのまま
  });
  return `{\n${entries.join(",\n")}\n}`;
};

この関数により、以下のようなオブジェクトが実行可能なJavaScriptコード文字列に変換されます。

{ getLanguage: () => "ja", getOS: () => "web" }

3.3 変数のスコープに関する注意点

関数内で使用される変数は、埋め込まれるJavaScriptコード内で定義する必要があります。
例えば、以下のようにモック内でfakeUserNameを使用する場合:

    getProfile: async () => ({
      domainId: fakeDomainId,
      userId: fakeUserName,
      displayName: fakeDisplayName,
    }),

この関数が文字列化されるとuserId: fakeUserNameという文字列になりますが、fakeUserNameという変数がブラウザ側で定義されていないと参照エラーになります。

そのため、body内で変数を明示的に定義しています:

const fakeUserName = faker.internet.userName();
// ... 省略

body: `    
	const fakeUserName = \`${fakeUserName}\`;   // 明示的に文字列内で定義して代入
    // ...
    window.woff = ${objToString(mockedWorksSdk)};
  `

4. テストでの使用方法

4.1 基本的な使用例

定義した setMockedWoffSdk  関数をページ遷移前に呼び出し、遷移時にモックされるように設定しておきます。 

import { test, expect } from '@playwright/test';
import { setMockedWoffSdk } from './utils';

test('テストページを表示する', async ({ page }) => {
  // WOFF SDKをモック
  await setMockedWoffSdk(page);
  // ページに移動
  await page.goto('/test-page');
  await expect(page.getByText('テスト')).toBeVisible();
});

4.2 カスタムモックオブジェクトの使用

特定のテストケースで異なる動作をシミュレートしたい場合、mockedObjectsパラメータを使用して部分的にモックをカスタマイズできます

  test("ログアウト状態をテストする", async ({ page }) => {
    // isLoggedInをカスタマイズ
    await setMockedWoffSdk(page, {
      isLoggedIn: () => false,
    });

    await page.goto("/test-page");
    await page.waitForFunction(() => window.woff !== undefined);

    // ログアウト状態を確認
    await expect(page.getByTestId("login-status")).toHaveText("ログアウト");
  });

Playwrightの画面: ログアウト表示に切り替わってることを確認
logout_test.png

  test("Android環境をテストする", async ({ page }) => {
    // OSをAndroidにカスタマイズ
    await setMockedWoffSdk(page, {
      getOS: () => "android",
    });

    await page.goto("/test-page");
    await page.waitForFunction(() => window.woff !== undefined);

    // Android OSが表示されることを確認
    await expect(page.getByText("android")).toHaveText("android");
  });

Playwrightの画面: OSが変化してることを確認
android_test.png

5. まとめ

力技でしたが、Playwrightのpage.routeメソッドを活用することで、scriptタグで読み込まれる外部SDKを効果的にモックできます。この手法は以下のような場合に有効です

  • 外部環境への依存を排除: 実際のWOFF環境がなくてもテストが可能
  • 様々なシナリオのテスト: ログイン状態、OS、言語など、様々な条件を簡単にシミュレート

この手法は、WOFF SDK以外の外部スクリプトをモックする場合にも応用できます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?