
PlaywrightにおけるFixtureと共通関数(クラス)の使い分け
はじめに
Playwrightを用いたテストの設計において、Fixtureと**共通関数(クラス)**のどちらを使うべきか迷う場面が多々あります。
- Fixtureはテストごとに決まった状態のPageオブジェクトを提供する
- 共通関数(またはクラス)は、状態を変化させながらテストを進めるのに適している
本記事では、それぞれの特徴や適用シーンを解説し、どのようなケースで使い分けるべきかを整理していきます。
Fixtureの特徴と適用シーン
Fixtureの概要
PlaywrightのFixtureは、テスト実行時に特定の状態を持つオブジェクト(Pageなど)を事前にセットアップし、
その状態をテスト間で再利用できる仕組みです。
Fixtureが適しているケース
-
テストごとに決まった初期状態を保証したい場合
- 例:ログイン済みの状態を前提としたテスト
- 例:特定の設定が適用された管理画面のテスト
-
セットアップのコストを削減したい場合
- 毎回ログイン操作を行うのではなく、一度セットアップした状態を使い回せる
-
データの一貫性を保ちたい場合
- 例:DBのデータが固定されている環境で、常に同じ条件でテストを実施したい
Fixtureのメリット
- 同じ状態の再利用ができるため、セットアップ時間を削減できる
- テストごとに初期状態を保証できるため、安定したテストが可能
- コードの再利用性が高い
Fixtureのデメリット
- 状態を変更する必要があるテストには向かない
- 例:ECサイトの購入フロー(カートに商品を追加 → 削除 → 追加)のように、状態が動的に変化するテストでは使いづらい
- 複雑になりがち
- 多くのFixtureを組み合わせると、どの状態がどこで設定されているのか把握しにくくなる
Fixtureの実装例
import { test as base } from '@playwright/test';
// 独自のFixtureを定義
const test = base.extend<{ loggedInPage: Page }>({
loggedInPage: async ({ page }, use) => {
await page.goto('/login');
await page.fill('#username', 'test_user');
await page.fill('#password', 'password123');
await page.click('button[type="submit"]');
await use(page);
}
});
test('ログイン後のダッシュボード表示', async ({ loggedInPage }) => {
await loggedInPage.goto('/dashboard');
await loggedInPage.waitForSelector('h1');
});
共通関数(クラス)の特徴と適用シーン
共通関数(クラス)の概要
共通関数やクラスを使うことで、状態が変化するテストシナリオを柔軟に記述できます。
共通関数(クラス)が適しているケース
-
テストごとに異なる状態を作りたい場合
- 例:ECサイトのカート機能のテスト(商品追加→削除→購入)
- 例:管理画面で設定を変更しながら動作確認するテスト
-
操作の流れをモジュール化したい場合
- 例:ログイン処理を共通関数にして、複数のテストで利用する
-
テストの柔軟性を重視したい場合
- 状態の変化を伴うテストをFixtureではなく、明示的な関数として記述することで可読性を向上させる
共通関数(クラス)のメリット
- 状態が変化するテストに適している
- テストの自由度が高い
- シンプルな構造で記述できるため、理解しやすい
共通関数(クラス)のデメリット
- ディレクトリ構造や命名規則を統一しないとカオスになる
- どの関数がどこにあるのか分かりにくくなる可能性がある
- コードの重複が発生する可能性がある
- Fixtureを適切に使えば再利用できるが、関数のコピペが増えるとメンテナンスコストが上がる
共通関数(クラス)の実装例
export class CheckoutFlow {
constructor(private page: Page) {}
async addToCart(itemId: string) {
await this.page.click(`#add-to-cart-${itemId}`);
}
async proceedToCheckout() {
await this.page.click('#checkout-button');
}
}
test('商品購入のテスト', async ({ page }) => {
const checkout = new CheckoutFlow(page);
await checkout.addToCart('item123');
await checkout.proceedToCheckout();
});
Fixtureと共通関数(クラス)の使い分けまとめ
項目 | Fixture | 共通関数(クラス) |
---|---|---|
状態の固定 | 向いている | 向いていない |
状態の変化を伴う処理 | 不向き | 向いている |
コードの管理 | 設計次第で複雑 | シンプルになりやすい |
初期状態の保証 | できる | できない |
テストの柔軟性 | 低い | 高い |
まとめ
- Fixture は「同じ状態を維持する」ことが重要なテストに適している。
- 例:ログイン済みの状態、設定が固定された画面のテスト
- 共通関数(クラス) は「状態を更新しながら進む」テストに適している。
- 例:ECサイトの購入フロー、設定変更を伴うテスト
- どちらを選ぶかは、テストの目的やメンテナンス性を考慮して決定する。
Playwrightのテスト設計を適切に行い、保守性の高いE2Eテストを構築していきましょう!