iT邦幫忙

2025 iThome 鐵人賽

DAY 10
0
IT 管理

Playwright + Test Design + AI Agent:自動化測試實戰系列 第 10

第 10 天:獨孤九劍的破劍式,一劍破萬法,Page Object Model (POM)

  • 分享至 

  • xImage
  •  

獨孤九劍的第一式,是「破劍式」,說的就是如何破解天下各門各派之劍法。在自動化測試領域裡,有一個非常著名的設計模式 POM (Page Object Model),是用來解決 UI 測試程式碼不好維護的問題,主要是操作 UI 會有不同的 selector 和動作 (Action) 需要管理,當 UI 元件太多則會讓程式碼變得雜亂不堪,不容易維護。

內功心法:Page Object Mode

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 類別,這個類別負責:

  1. 定義頁面上的元素 Locator
  2. 封装該頁面可被執行的操作 methods
    這樣測試案例就只需要呼叫 Page Object 的方法,而不需要關注於底層的 Selector。

招式演練:

讓我們重新改寫上面的範例,首先:

  1. 建立 Page Object
  2. 重構原本的測試案例

建立 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 更容易整合並且產生對應的測試程式碼。

秘笈傳授:

  1. 每個頁面只會有一個類別,如果頁面上太多元件,可以考慮拆成元件類別 (Compoent Class)。
  2. Selector 使用 data-testid 或是使用 getRole() 代替
  3. 方法命名語意化,例如:使用 Login()Logout() 等等,能夠透過函式名稱理解程式碼的行為
  4. 保持單一職責,Page Object 僅負責 UI 互動,如果跟驗證 (Assertion) 有關則是寫在測試程式碼中
  5. 配合 Fixtures 提供 Page Object,能夠建少重複建立程式碼

收功:今日總結

今天,我們學習了獨孤九劍的「破劍式」,也就是 Page Object Model (POM) 這套設計模式。這套劍法,是為了解決 UI 測試中因為 UI 容易變動且難易維護的痛點。在沒有 POM 的情況下,我們的測試程式碼會需要許多的 selector。一旦頁面需要改動,所有的測試案例都可能會失敗。而 POM 則提供了一套優雅的解決方案:它將每個頁面抽象化為一個獨立的物件,負責封裝所有頁面元素的 Locator 與可執行的動作。如此一來,我們的測試案例便能專注於行為本身,例如 loginPage.login(),而無需關心底層 Selector 的細節。

這不僅讓測試程式碼更具可讀性與語意化,也大幅降低了維護成本。當介面變動時,我們只需在對應的 Page Object 類別中修改,即可讓所有相關測試恢復正常,真正做到「一劍破萬法」。透過 POM 的協助,我們的測試案例變得乾淨、整潔,更易於 AI 協作。它不再是簡單的指令集合,而是對使用者行為的清晰描述,讓測試案例執行起來更加穩定。


上一篇
第 09 天:獨孤九劍的總訣式 - Fixtures
下一篇
第 11 天:獨孤九劍的破刀式,以輕禦重,以快制慢 - Page Factory
系列文
Playwright + Test Design + AI Agent:自動化測試實戰12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言