自動テストチュートリアル

Playwright、Seleniumなどの自動テストツールを学ぼう


title: 演習: フォームページのPOM化

演習: フォームページのPOM化

複雑なフォームページをPage Object化してみましょう。

対象ページ

会員登録フォームを想定します:

  • 名前(テキスト)
  • メールアドレス(テキスト)
  • 性別(ラジオボタン)
  • 興味のあるカテゴリ(チェックボックス複数)
  • 都道府県(セレクトボックス)
  • 利用規約同意(チェックボックス)
  • 登録ボタン

演習: Page Objectを作成

以下の要件を満たすPage Objectを作成してください。

要件

  1. 各フォーム要素をLocatorとして定義
  2. フォーム全体を一度に入力できるメソッド
  3. 各項目を個別に入力できるメソッド
  4. 複数のチェックボックスを選択できるメソッド

解答例

クリックして解答を表示
// pages/RegistrationPage.ts
import { Page, Locator } from '@playwright/test';

interface RegistrationData {
  name: string;
  email: string;
  gender: 'male' | 'female' | 'other';
  categories: string[];
  prefecture: string;
  agreeToTerms: boolean;
}

export class RegistrationPage {
  readonly page: Page;
  readonly nameInput: Locator;
  readonly emailInput: Locator;
  readonly maleRadio: Locator;
  readonly femaleRadio: Locator;
  readonly otherRadio: Locator;
  readonly prefectureSelect: Locator;
  readonly termsCheckbox: Locator;
  readonly submitButton: Locator;
  readonly successMessage: Locator;

  constructor(page: Page) {
    this.page = page;
    this.nameInput = page.getByRole('textbox', { name: '名前' });
    this.emailInput = page.getByRole('textbox', { name: 'メールアドレス' });
    this.maleRadio = page.getByRole('radio', { name: '男性' });
    this.femaleRadio = page.getByRole('radio', { name: '女性' });
    this.otherRadio = page.getByRole('radio', { name: 'その他' });
    this.prefectureSelect = page.getByRole('combobox', { name: '都道府県' });
    this.termsCheckbox = page.getByRole('checkbox', { name: '利用規約に同意' });
    this.submitButton = page.getByRole('button', { name: '登録' });
    this.successMessage = page.locator('.success-message');
  }

  async goto() {
    await this.page.goto('/register');
  }

  // 名前を入力
  async fillName(name: string) {
    await this.nameInput.fill(name);
  }

  // メールを入力
  async fillEmail(email: string) {
    await this.emailInput.fill(email);
  }

  // 性別を選択
  async selectGender(gender: 'male' | 'female' | 'other') {
    const radioMap = {
      male: this.maleRadio,
      female: this.femaleRadio,
      other: this.otherRadio,
    };
    await radioMap[gender].check();
  }

  // カテゴリを選択(複数)
  async selectCategories(categories: string[]) {
    for (const category of categories) {
      await this.page.getByRole('checkbox', { name: category }).check();
    }
  }

  // 都道府県を選択
  async selectPrefecture(prefecture: string) {
    await this.prefectureSelect.selectOption(prefecture);
  }

  // 利用規約に同意
  async agreeToTerms() {
    await this.termsCheckbox.check();
  }

  // フォームを送信
  async submit() {
    await this.submitButton.click();
  }

  // フォーム全体を入力
  async fillForm(data: RegistrationData) {
    await this.fillName(data.name);
    await this.fillEmail(data.email);
    await this.selectGender(data.gender);
    await this.selectCategories(data.categories);
    await this.selectPrefecture(data.prefecture);
    if (data.agreeToTerms) {
      await this.agreeToTerms();
    }
  }

  // 登録を完了
  async register(data: RegistrationData) {
    await this.fillForm(data);
    await this.submit();
  }
}

テスト例

import { test, expect } from '@playwright/test';
import { RegistrationPage } from '../pages/RegistrationPage';

test('会員登録ができる', async ({ page }) => {
  const registrationPage = new RegistrationPage(page);
  await registrationPage.goto();

  await registrationPage.register({
    name: '田中太郎',
    email: 'tanaka@example.com',
    gender: 'male',
    categories: ['スポーツ', '音楽'],
    prefecture: '東京都',
    agreeToTerms: true,
  });

  await expect(registrationPage.successMessage).toBeVisible();
});
💡 ポイント
  • interfaceでフォームデータの型を定義すると使いやすい
  • 個別入力と一括入力の両方のメソッドを用意する
  • 複数選択は配列で受け取ってループで処理