獨孤九劍的第一式,是「破劍式」,說的就是如何破解天下各門各派之劍法。在自動化測試領域裡,有一個非常著名的設計模式 POM (Page Object Model),是用來解決 UI 測試程式碼不好維護的問題,主要是操作 UI 會有不同的 selector 和動作 (Action) 需要管理,當 UI 元件太多則會讓程式碼變得雜亂不堪,不容易維護。
Page Object 是一個撰寫 UI 自動化測試所使用的一種設計模式 (Design Pattern),透過將網頁元素抽象化成元件的實作方式,讓程式碼變得可讀性、可維護、可重用。在沒有使用 POM 的情況下,測試案例往往會執行操作 Selector,以下列使用者成功登入的為例:
// tests/login.spec.ts
import { test, expect } from '@playwright/test';
test('使用者可以成功登入', async ({ page }) => {
await page.goto('https://your-app.example.com/login');
await page.getByTestId('login-username').fill('user01');
await page.getByTestId('login-password').fill('pass123');
await page.getByTestId('login-submit').click();
await expect(page).toHaveURL(/home/);
});
當如果登入頁面修改了 Select,就必須在所有的測試檔案中修改,維護成本變高。在 POM 中,每個頁面或者是頁面的一部分都會對應到一個 Page Object 類別,這個類別負責:
讓我們重新改寫上面的範例,首先:
// pages/LoginPage.ts
import { Page, Locator } from '@playwright/test';
export class LoginPage {
readonly page: Page;
readonly usernameInput: Locator;
readonly passwordInput: Locator;
readonly submitButton: Locator;
constructor(page: Page) {
this.page = page;
this.usernameInput = page.getByTestId('login-username');
this.passwordInput = page.getByTestId('login-password');
this.submitButton = page.getByTestId('login-submit');
}
async goto() {
await this.page.goto('https://your-app.example.com/login');
}
async login(username: string, password: string) {
await this.usernameInput.fill(username);
await this.passwordInput.fill(password);
await this.submitButton.click();
}
}
// tests/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
test('使用者可以成功登入', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('user01', 'pass123');
await expect(page).toHaveURL(/home/);
});
重新閱讀重構之後的測試案例,你會發現我們從原本的 Locator 行為轉換成測試案例的行為 (ACT),所有與登入頁面有關的操作都可以透過 Page Object 的操作,除非測試案例的行為改變,我們才需要修改測試案例,這樣可以降低維護程式碼的成本並且我們能夠清楚閱讀測試案例的語意,更貼合測試案例的需求描述。透過 POM 封装起來的類別,因為頁面的操作被封装後,可以讓 LLM 更容易整合並且產生對應的測試程式碼。
data-testid
或是使用 getRole()
代替Login()
、Logout()
等等,能夠透過函式名稱理解程式碼的行為今天,我們學習了獨孤九劍的「破劍式」,也就是 Page Object Model (POM) 這套設計模式。這套劍法,是為了解決 UI 測試中因為 UI 容易變動且難易維護的痛點。在沒有 POM 的情況下,我們的測試程式碼會需要許多的 selector。一旦頁面需要改動,所有的測試案例都可能會失敗。而 POM 則提供了一套優雅的解決方案:它將每個頁面抽象化為一個獨立的物件,負責封裝所有頁面元素的 Locator 與可執行的動作。如此一來,我們的測試案例便能專注於行為本身,例如 loginPage.login(),而無需關心底層 Selector 的細節。
這不僅讓測試程式碼更具可讀性與語意化,也大幅降低了維護成本。當介面變動時,我們只需在對應的 Page Object 類別中修改,即可讓所有相關測試恢復正常,真正做到「一劍破萬法」。透過 POM 的協助,我們的測試案例變得乾淨、整潔,更易於 AI 協作。它不再是簡單的指令集合,而是對使用者行為的清晰描述,讓測試案例執行起來更加穩定。