iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
IT 管理

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

第 18 天:獨孤九式總結 - 可維護的測試程式碼

  • 分享至 

  • xImage
  •  

獨孤九式總結,將近花費十天講述如何撰寫可維護性的測試程式碼,其目的是為了讓 AI 能夠撰寫可維護的測試程式碼,並且理解什麼是可維護性的測試案例。

內功心法:測試可維護性與重構

為什麼測試可維護性很重要

根據專案經驗,自動化測試案例會隨著功能改變而需要動態調整。如果測試程式碼相依性很高,可能會導致明明只修改一個功能,卻連帶使其他測試案例失敗,甚至出現 Flaky Test 的情況。這不僅會讓測試人員花費大量時間排查問題,也可能影響整個團隊對自動化測試的信任。一個良好的測試案例應當是易於維護的,主要基於以下幾點:

  • 語意可讀性高:即使不是專門負責該功能的人員,也能快速理解測試意圖。
  • 低耦合:測試邏輯與被測試系統的實作細節應該分開,這與語意可讀性息息相關。
  • 可重複使用:將常用的操作或功能封裝成共用模組,避免相同程式碼散落在專案各處。
  • 易於修改:當需求或 UI 改動時,只需修改少量程式碼即可完成。

撰寫測試程式碼的 Anti-Pattern

接下來,我們來討論會導致測試案例難以維護的常見問題,也就是「反模式 (Anti-Pattern)」:

  1. 將 Selector 寫死,而不是使用穩定的唯一條件。
    await page.click('#submit123'); // 當元素 ID 一修改,所有相關測試案例都會失敗。
    
  2. 測試資料分散在不同地方,導致需要重複定義相同的帳號、密碼或資料。
  3. 當測試資料需要修改時,必須進行全域搜尋取代。
  4. 測試邏輯重複,沒有抽象化與共用。
  5. 登入流程在多個檔案中重複實作。
  6. 過度依賴固定等待時間。
    await page.waitForTimeout(3000); // 不穩定,且增加了不必要的執行時間。
    
  7. 缺乏結構化設計,沒有使用 Page Object Model 或 Fixtures。
  8. 測試與環境設定混雜在一起,難以管理。

招式演練:重構策略與範例

範例:使用者登入 (未重構版)

// login.spec.ts
test('User login', async ({ page }) => {
  await page.goto('https://app.example.com/login');
  await page.fill('#username', 'user01');
  await page.fill('#password', 'pass123');
  await page.click('#login-btn');
  await expect(page).toHaveURL('https://app.example.com/home');
});

這段程式碼的主要問題在於:Selector 寫死、登入流程與測試邏輯耦合,且無法重用。

範例:重構後(Page Object Model)

// pages/LoginPage.ts
export class LoginPage {
  constructor(private page) {}
  async goto() {
    await this.page.goto('/login');
  }
  async login(username: string, password: string) {
    await this.page.getByTestId('login-username').fill(username);
    await this.page.getByTestId('login-password').fill(password);
    await this.page.getByTestId('login-submit').click();
  }
}
// login.spec.ts
import { LoginPage } from '../pages/LoginPage';

test('User login', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
  await loginPage.login('user01', 'pass123');
  await expect(page).toHaveURL('/home');
});

Page Object Model 的核心優勢

Page Object Model 的價值在於它能在測試邏輯和使用者介面細節之間建立一道防火牆,帶來以下三大優勢:

  • Selector 集中管理:想像一下,如果登入按鈕從 #login-btn 改為 #sign-in-button,你只需要在 LoginPage.ts 這個單一檔案中修改。所有依賴它的測試案例都會自動更新,大大降低了維護成本。
  • 登入流程可在多個測試中重用:在一個大型專案中,許多測試都需要從登入開始。Page Object Model 將這個流程封裝成一個簡單的函數,例如 loginPage.login(),讓你的測試程式碼更簡潔,也更容易重複使用。
  • UI 變動時只需修改一處:這是對前兩點的總結。這種低耦合的特性,讓你的測試套件能夠從容應對頻繁的 UI 變化,確保自動化測試的長期穩定性。

最佳實務

  • 分離測試邏輯與 UI:使用 Page Object ModelComponent Object 將「要做什麼」的測試邏輯,與「怎麼做」的 UI 實作細節分開。
  • 使用 Fixtures 管理測試資料與環境設定:Fixtures 就像是測試的「工具箱」,它集中管理所有測試所需的預設狀態和資料,確保每個測試都在一個乾淨、可預期的環境中運行。
  • 將常用動作抽成 Helper Functions:避免程式碼重複。將創建測試用戶、清空購物車等常用步驟封裝成獨立函數,讓程式碼更乾淨、更具可讀性。
  • 使用 data-testid 作為穩定的選擇器data-testid 是一個專為測試而生的 HTML 屬性,它提供一個穩定的錨點給測試框架,讓你的測試腳本不易因 UI 外觀改變而中斷。
  • 避免使用固定等待時間,改用狀態斷言:放棄不穩定且浪費時間的 await page.waitForTimeout(),改用 狀態斷言,例如 expect(page).toBeVisible()expect(page).toHaveURL()。它們會智慧地等待直到條件滿足,確保測試的穩定與效率。
  • 定期重構,確保長期品質:測試維護不是一次性任務,而是一個持續的過程。定期安排時間審視並優化測試程式碼,就像給汽車做定期保養,能確保你的測試套件隨著專案成長,持續保持其效率與可靠性。

收功:今日總結

測試的可維護性與重構,是確保自動化測試能真正發揮長期價值的關鍵。當你的測試程式碼變得易於閱讀、修改與重用時,它將不再是負擔,而成為團隊快速迭代、自信發布的堅實後盾。


上一篇
第 17天:獨孤九劍的破索式 - 資料驅動測試
下一篇
第 19 天:葵花寶典 - 欲練此功,必先自宮
系列文
Playwright + Test Design + AI Agent:自動化測試實戰20
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言