はじめに
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.fulfillのbodyパラメータには文字列を渡す必要があります。そのため、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の画面: ログアウト表示に切り替わってることを確認

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");
});
5. まとめ
力技でしたが、Playwrightのpage.routeメソッドを活用することで、scriptタグで読み込まれる外部SDKを効果的にモックできます。この手法は以下のような場合に有効です
- 外部環境への依存を排除: 実際のWOFF環境がなくてもテストが可能
- 様々なシナリオのテスト: ログイン状態、OS、言語など、様々な条件を簡単にシミュレート
この手法は、WOFF SDK以外の外部スクリプトをモックする場合にも応用できます。
