iT邦幫忙

2025 iThome 鐵人賽

DAY 11
0
IT 管理

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

第 11 天:獨孤九劍的破刀式,以輕禦重,以快制慢 - Page Factory

  • 分享至 

  • xImage
  •  

獨孤九劍的「破刀式」是一招破解單刀(在單刀中又有快刀法)、雙刀、柳葉刀、鬼頭刀、大砍刀、斬馬刀等種種刀法。昨天,講到 POM ,但其實隨著網頁越來越複雜,單一網頁可能會產生多個 Page Object,造成管理上的困難,今天我想要介紹 POM 的另外一種變體,Page Factory 用來解決 POM 在建立時的維護成本。

內功心法:什麼是 Page Factory

Page Factory 是 Page Object Model(POM)的一種進階變體,主要解決 POM 類別建立與維護上的重複與手動管理成本。它的核心思想是:由一個統一的工廠類別(Factory)負責建立、管理並回傳所需的 Page Object 實例,而不是在每個測試檔案中手動 new 一個頁面物件。

為什麼需要 Page Factory

雖然 POM 能提高可維護性,但當專案頁面越多時,會出現一些痛點:

  • 測試案例中需要重複建立不同的 Page Object
  • 在不同測試案例傳遞 Page 物件的方式不一樣
  • 撰寫測試程式碼需要 Page Object 的具體類別和建置方法

使用 Page Factory 透過集中化管理,可以幫助我們:

  1. 統一建立 Page Object 的邏輯
  2. 降低測試程式碼對於類別細節的依賴
  3. 更容易替換與擴充頁面類別

招式演練:Page Factory 的基本結構

在下面這個例子,我們主要會有首頁和登入頁面兩個 Page Object 類別:

建立 Login Page 和 Home Page

// pages/LoginPage.ts
import { Page, Locator } from '@playwright/test';

export class LoginPage {
  constructor(private page: Page) {}
  usernameInput = this.page.getByTestId('login-username');
  passwordInput = this.page.getByTestId('login-password');
  submitButton = this.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();
  }
}
// pages/HomePage.ts
import { Page } from '@playwright/test';

export class HomePage {
  constructor(private page: Page) {}
  async isLoaded() {
    return this.page.getByRole('heading', { name: '首頁' }).isVisible();
  }
}

建立 Page Factory

// pages/PageFactory.ts
import { Page } from '@playwright/test';
import { LoginPage } from './LoginPage';
import { HomePage } from './HomePage';

export class PageFactory {
  constructor(private page: Page) {}

  loginPage() {
    return new LoginPage(this.page);
  }

  homePage() {
    return new HomePage(this.page);
  }
}

在測試程式碼中使用 Page Factory

// tests/login.spec.ts
import { test, expect } from '@playwright/test';
import { PageFactory } from '../pages/PageFactory';

test('使用者可以成功登入並進入首頁', async ({ page }) => {
  const factory = new PageFactory(page);

  const loginPage = factory.loginPage();
  await loginPage.goto();
  await loginPage.login('user01', 'pass123');

  const homePage = factory.homePage();
  expect(await homePage.isLoaded()).toBeTruthy();
});

秘笈傳授:使用 Page Factory 的優點

使用 Page Factory 的優點

  • 統一管理超方便:不用在每個測試檔案裡,都自己一個一個去 new 出 Page Object。只要呼叫對應的工廠類別就好,所有頁面物件的建立都會集中管理。
  • 程式碼更簡潔:閱讀程式碼的人,並不用知道 Page Object 到底是怎麼被建立出來的,這樣寫出來的程式碼會更乾淨、更專注在測試邏輯上。
  • 擴充性超好:如果之後網站多了一個新頁面,只要在工廠類別裡面多加一個方法就好,其他測試程式碼完全不用動到,增加維護性。
  • 搭配 Fixtures 更強大:你可以直接在 beforeEach 裡面,透過 Fixtures 建立好 Page Factory,並在每個測試案例中使用。

最佳實踐

清楚的命名方式

一個好的命名能讓程式碼更具可讀性。對於 Page Factory,你可以給它一個清晰的名字,例如 PageObjectManager 或 PageFactory。而其內部的方法也應該語意化,清楚表達其所回傳的頁面物件,例如:getLoginPage()、getHomePage()。

保持單一職責

單一職責原則 (SRP) 是 Page Factory 的核心。它唯一的職責就是「建立與管理」頁面物件。它不應該包含任何與測試邏輯、驗證 (Assertion) 或是資料處理相關的程式碼。將這些邏輯分開,能確保你的測試架構清晰、易於維護。

隨時可以依賴注入 (Dependency Injection)

可以透過我們在第九天學到的 Fixtures,來提供 Page Factory 的實例。這樣一來,你的測試案例就不需要手動 new 出一個工廠,而是可以直接從 fixture 中取得,讓程式碼更乾淨、更具彈性。

收功:今日總結

Page Factory 是一種進階版的 Page Object Model (POM),主要用來解決當網頁數量與複雜度增加時,由 POM 衍生的管理問題。

如果說 POM 是將雜亂的程式碼抽象化,那 Page Factory 則是將 Page Object 類別的實例化過程集中管理。它就像一個統一的工廠,負責建立並回傳所需的頁面物件,讓測試案例可以直接向它索取,而不需要手動創建,這大大降低了開發與維護成本。透過 Page Factory,我們的測試程式碼會更乾淨、更專注於測試邏輯本身,並且與 Page Object 的細節解耦。這個模式提高了程式碼的可讀性和擴充性。當需要新增或替換頁面時,只需在 Factory 類別中調整,就能避免影響到其他測試案例。

將 Page Factory 與 Fixtures 結合,能進一步提升效率。我們可以透過 Fixture 在每個測試開始前,自動建立一個 Page Factory 實例,確保每個測試都能在一致且穩定的環境中執行。簡單的說,掌握了 Page Factory,你便不再需要為了管理頁面物件而煩惱,而是能將精力專注於核心測試邏輯。它能讓你的測試程式碼更為簡潔、更有力。


上一篇
第 10 天:獨孤九劍的破劍式,一劍破萬法,Page Object Model (POM)
下一篇
第 12 天:獨孤九劍的破槍式 - Loadable Component
系列文
Playwright + Test Design + AI Agent:自動化測試實戰12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言