はじめに
テスト自動化を行う上で、テストコードはプロダクションコードと同様に、コードの保守性・可読性はとても重要であり、単純にテストを「動かす」だけでは、アプリケーションが大きくなるにつれてテストコードも複雑化し、修正や再利用が難しくなる。
Playwright を使った E2E テストでも同様で、テストコードが肥大化すると
- 同じ操作が何度も書かれる
- UI が変更されると複数箇所を修正する必要がある
- テストの可読性が下がる
といった問題が発生する。
こうした課題を解決するのが POM(Page Object Model) というデザインパターンである。
本記事では、Playwright における POM の基本概念、活用した実装例、メリットについて整理していこうと思う。
※ 環境構築等の事前準備に関しての記述は省略しています
POM(Page Object Model)とは
POM は、「1つのページごとに操作と要素をまとめたクラスを作る」デザインパターンである。
ポイント
- UI 要素と操作をクラスにカプセル化
- テストコードから UI 詳細を隠蔽
- 再利用性・保守性の向上
Playwright における POM の基本構成
- Page クラス
- Locator : ページの要素
- 操作メソッド : ページの要素を用いて操作を行わせる
- テストクラス
- Page クラスの操作メソッドを呼び出してテスト実行
POM を活用した実装例
前述した POM の基本概念を活用して、実際に Playwright の実装例を以下で記述する。
言語 : java
ポイントとして、基本概念を前提に工夫した部分は以下
- Page クラスの Locator : ページの要素に関しては、可読性を高めるために別クラスに各ページごとで作成する方針とした
- 各ページで共通する部分は BasePage として作成
- 各テストで共通する処理は BaseTest として作成
Locator クラス (ログインページの要素定義)
package playwright.locator;
public class LoginPageLocator {
public static final String USER_NAME = "#username";
public static final String PASSWORD = "#password";
public static final String LOGIN_BUTTON = "#login_button";
}
HTML は以下想定(部分抜粋)
<form id="login_form">
<label for="username">ユーザー名</label>
<input type="text" id="username" name="username" />
<label for="password">パスワード</label>
<input type="password" id="password" name="password" />
<button id="login_button" type="submit">ログイン</button>
</form
Page クラス (ログインページの例)
package playwright.page;
import com.microsoft.playwright.Page;
import playwright.locator.LoginPageLocator;
public class LoginPage extends BasePage {
public LoginPage(Page page) {
super(page);
}
// ユーザー名を入力
public void inputUsername(String username) {
page.locator(LoginPageLocator.USER_NAME).fill(username);
}
// パスワードを入力
public void inputPassword(String password) {
page.locator(LoginPageLocator.PASSWORD).fill(password);
}
// ログインボタンをクリック
public void clickLoginButton() {
page.locator(LoginPageLocator.LOGIN_BUTTON).click();
}
// ログイン操作をまとめたメソッド
public void login(String username, String password) {
inputUsername(username);
inputPassword(password);
clickLoginButton();
}
}
BasePage クラス (共通処理)
package playwright.page;
import com.microsoft.playwright.Page;
public class BasePage {
protected Page page;
public BasePage(Page page) {
this.page = page;
}
// 数秒待機
protected void sleep() throws InterruptedException {
Thread.sleep(500);
}
// スクリーンショットを取得し任意のパスに配置
protected void screenshot(String name) {
page.screenshot(new Page.ScreenshotOptions()
.setPath(Paths.get("任意のパス" + name + ".png"))
.setFullPage(true)
);
}
}
テストクラス (Page クラスを使用)
package playwright.test;
import com.microsoft.playwright.*;
import org.junit.jupiter.api.*;
import playwright.page.LoginPage;
public class Test extends BaseTest {
@Test
void テスト() throws InterruptedException {
// ログイン画面
LoginPage loginPage = new LoginPage(page);
loginPage.login("ユーザーネーム", "パスワード");
// 次の画面
// 同様に Page クラス等を作成して、実装していく
}
}
BaseTest クラス
package playwright.test;
import com.microsoft.playwright.*;
import org.junit.jupiter.api.*;
public class BaseTest {
protected Browser browser;
protected BrowserContext context;
protected Page page;
@BeforeEach
void setUp() {
// ブラウザの起動
browser = Playwright.create().chromium().launch(
new BrowserType.LaunchOptions().setHeadless(false)
);
// コンテキストとページの作成
context = browser.newContext();
page = context.newPage();
}
@AfterEach
void tearDown() {
page.close();
context.close();
browser.close();
}
}
おまけ
UI で表示されている文字検証をする場合、一例ですが該当の Page クラスに以下のようなメソッドをきさいしテストクラスで戻り値の検証を実施する、もしくはこのクラスで検証までしてしまう。
もっとよい設計はある気がしているが。
public boolean isMatchMessage() {
String actual = page.locator(HomePageLocator.MESSAGE).textContent().trim();
return actual.equals("期待値")
}
上記の方針で POM を活用したメリット
-
保守性向上
UI が変わった場合も基本的に Locator クラス内だけ修正すればテスト全体に反映される
-
再利用性
同じ操作を複数のテストで呼び出せる
-
可読性向上
テストコードから UI 詳細を隠せるので、テストの意図が明確になる
まとめ
-
POM(Page Object Model)は、1ページごとに要素と操作をクラス化するデザインパターン
→ テストコードの保守性・再利用性・可読性を大幅に向上させる -
Page クラス、Locator クラスと分けることで、UI 変更時の修正箇所を最小化できる
→#usernameや#login_buttonのようなセレクタが変更されても、Locator クラスを修正するだけでテスト全体に反映可能 -
BasePage / BaseTest を活用すると共通処理をまとめられる
→ ページ遷移やスクリーンショットなど、テスト通して繰り返し共通して実施する操作を切り出し -
テストクラスは操作と検証を簡潔に記述できる
→ Page クラスを呼び出すだけで、テストコードは読みやすく、UI 変更にも強い
参考
-
Playwright POM
https://playwright.dev/docs/pom -
Playwright Locator API (Java)
https://playwright.dev/java/docs/api/class-locator -
POM についての解説記事
https://zenn.dev/izumi_ren/articles/aa3aa432f33b69