Playwright基礎篇(三):測試案例實作

實作第一個測試:Apple網站購物流程
流程分析與測試目標
在開始編寫測試腳本前,我們需要先清楚定義測試目標和流程。對於本範例,我們將模擬用戶在 Apple 官網選購 iPad 的完整流程:
- 訪問 Apple 官網
- 導航至 iPad 產品頁面
- 選擇特定 iPad 型號(11吋 iPad)
- 選擇產品規格(顏色、儲存容量等)
- 加入購物車
- 驗證購物車內容是否正確
這個測試案例涵蓋了典型的電子商務網站核心功能,對於小企業的網站測試有很高的參考價值。
前置準備工作
在開始編寫測試前,我們需要完成以下準備工作:
- 建立專案結構:我們將採用頁面物件模式(Page Object Model)來組織代碼,這樣可以提高代碼的可維護性和重用性。
- 頁面物件分析:識別測試中涉及的主要頁面元素,例如:
- 顏色選擇器
- 儲存容量選項
- 連線類型選項(Wi-Fi)
- 各種附加選項(雕刻、Apple Pencil、鍵盤等)
- 購買選項
- 「加入購物袋」按鈕
- 建立適當的文件結構:
my-playwright-project/
├── tests/
│ └── example.spec.ts # 測試腳本
├── pages/
│ └── ProductPage.ts # 產品頁面物件
└── ...
編寫測試腳本
讓我們看看如何實現整個測試流程。
訪問 Apple 官網與導航
首先,我們需要建立測試的基本結構並導航至 iPad 產品頁面:
// tests/example.spec.ts
import { test, expect } from '@playwright/test';
import { ProductPage } from '../pages/ProductPage.ts';
test('Add to Bag Flow', async ({ page }) => {
const productPage = new ProductPage(page);
// Step 1: Go to iPad page
await page.goto('https://www.apple.com/');
await page.getByLabel('iPad', { exact: true }).click();
await page.getByRole('link', { name: 'iPad New' }).click();
await page.getByRole('link', { name: 'Pre-order iPad 11-inch' }).click();
// 後續步驟將在這裡加入...
});
這段代碼做了以下事情:
- 導入必要的 Playwright 測試工具
- 導入我們自定義的 ProductPage 頁面物件
- 創建一個名為 ‘Add to Bag Flow’ 的測試
- 訪問 Apple 官網
- 依次點擊導航至 iPad 產品頁面的元素
產品頁面物件定義
在編寫測試腳本之前,我們先定義產品頁面物件,以便更好地組織和重用代碼:
// pages/ProductPage.ts
import { expect, Locator, Page } from '@playwright/test';
export class ProductPage {
readonly page: Page;
readonly colorLocator: Locator;
readonly storageLocator: Locator;
readonly wifiLocator: Locator;
readonly engravingLocator: Locator;
// 依照命名規則向下完成readonly的定義...
constructor(page: Page) {
this.page = page;
this.colorLocator = page.locator('#root').locator('text="Pink"');
this.storageLocator = page.locator('#root').locator('text="256GB"');
this.wifiLocator = page.locator('#root').locator('text="Wi-FiEvery iPad can connect"');
this.engravingLocator = page.locator('text="No engraving"');
}
// 方法定義將在下方繼續...
}
頁面物件包含了頁面上的所有關鍵元素定位器,這些定位器使用了 Playwright 的多種選擇器方式,如文本、角色等。
選擇產品規格
現在,讓我們實現產品頁面物件中的方法,用於選擇各種產品規格:
// ProductPage.ts 中的方法
async selectColor(color: string) {
const colorLocator = this.page.locator(`text="${color}"`).first();
await colorLocator.waitFor({ state: 'visible', timeout: 10000 });
await colorLocator.click();
}
async selectStorageOption(storage: string) {
// 等待存儲選項出現
await this.page.waitForSelector(`input[name="dimensionCapacity"][value="${storage.toLowerCase()}"]`, { timeout: 20000 });
// 獲取存儲容量的 radio 按鈕元素
const storageLocator = this.page.locator(`input[name="dimensionCapacity"][value="${storage.toLowerCase()}"]`);
// 確保元素可見
await storageLocator.waitFor({ state: 'visible', timeout: 20000 });
// 確保元素滾動到視口內
await storageLocator.scrollIntoViewIfNeeded();
// 強制點擊(如果正常點擊不起作用時使用)
await storageLocator.click({ force: true });
}
async selectWiFiOption() {
try {
// 使用 input 元素的 value 屬性定位 Wi-Fi 選項
const wifiLocator = this.page.locator('input[name="dimensionConnection"][value="wifi"]');
// 等待元素可見
await wifiLocator.waitFor({ state: 'visible', timeout: 20000 });
// 滾動元素到視口內
await wifiLocator.scrollIntoViewIfNeeded();
// 確保元素可見
await wifiLocator.waitFor({ state: 'visible', timeout: 20000 });
// 確保元素滾動到視口內
await wifiLocator.scrollIntoViewIfNeeded();
// 強制點擊(如果正常點擊不起作用時使用)
await wifiLocator.click({ force: true });
} catch (error) {
console.error('Error selecting Wi-Fi option:', error);
throw error;
}
}
// 其他選項的方法...
async selectEngravingOption() {
await this.engravingLocator.click();
}
// 依此類推...
這些方法包含了:
- 智能等待(確保元素加載和可見)
- 錯誤處理
- 滾動頁面(確保元素在視口內)
選擇付款選項並加入購物袋
接下來,實現選擇付款方式和加入購物袋的方法:
// ProductPage.ts 中的更多方法
async selectPurchaseOption() {
// 等待 "fullprice" 選項可見,確保頁面完全加載
const purchaseLocator = this.page.locator('input[name="purchase_option_group"][value="fullprice"]');
await purchaseLocator.waitFor({ state: 'visible', timeout: 20000 });
// 確保購買選項滾動到視口內
await purchaseLocator.scrollIntoViewIfNeeded();
// 點擊該購買選項
await purchaseLocator.check(); // 適用於 radio 按鈕
}
async selectAppleCareOption() {
await this.appleCareLocator.click();
}
async addToBag() {
const addToBagButton = this.page.getByRole('button', { name: 'Add to Bag' });
await addToBagButton.waitFor({ state: 'visible', timeout: 10000 });
await addToBagButton.click();
}
async verifyProductInBag(productName: string) {
await expect(this.page.locator(`text="${productName}"`)).toBeVisible();
}
這些方法處理了:
- 選擇全額購買選項(而非分期付款)
- 選擇是否添加 AppleCare+
- 點擊「加入購物袋」按鈕
- 驗證產品是否成功加入購物袋
完整測試流程
現在,我們將所有步驟組合起來,完成整個測試腳本:
// tests/example.spec.ts 完整版
import { test, expect } from '@playwright/test';
import { ProductPage } from '../pages/ProductPage.ts';
test('Add to Bag Flow', async ({ page }) => {
const productPage = new ProductPage(page);
// Step 1: Go to iPad page
await page.goto('https://www.apple.com/');
await page.getByLabel('iPad', { exact: true }).click();
await page.getByRole('link', { name: 'iPad New' }).click();
await page.getByRole('link', { name: 'Pre-order iPad 11-inch' }).click();
// Step 2: Perform actions on ProductPage
await productPage.selectColor('Pink'); // 選擇粉色
await productPage.selectStorageOption('512GB'); // 選擇 512GB 儲存容量
await productPage.selectWiFiOption(); // 選擇 Wi-Fi 選項
// 驗證 Wi-Fi 選項是否已選中
const wifiInput = page.locator('input[name="dimensionConnection"][value="wifi"]:checked');
await expect(wifiInput).toBeChecked();
await productPage.selectEngravingOption(); // 選擇不刻字
await productPage.selectApplePencilOption(); // 選擇不購買 Apple Pencil
await productPage.selectKeyboardOption(); // 選擇不購買鍵盤
await productPage.selectTradeInOption(); // 選擇不折抵
// 選擇「全額付款」購買選項
await productPage.selectPurchaseOption();
// 驗證「全額付款」選項是否已選中
const purchaseInput = page.locator('input[name="purchase_option_group"][value="fullprice"]:checked');
await expect(purchaseInput).toBeChecked();
await productPage.selectAppleCareOption(); // 選擇不購買 AppleCare+
await productPage.addToBag(); // 加入購物袋
// Step 3: 驗證產品是否已成功加入購物袋
await productPage.verifyProductInBag('11-inch iPad Wi-Fi 512GB - Pink');
// 截圖
const generateScreenshotName = () => `screenshot_${Date.now()}.png`;
await page.screenshot({ path: generateScreenshotName() });
});
測試執行與觀察
執行測試腳本
要執行我們剛剛創建的測試,請在終端機中輸入:
npx playwright test
如果你希望以視覺化方式觀察測試過程,可以添加 --headed
選項:
npx playwright test --headed
對於調試目的,你還可以使用 --debug
選項,這將允許你逐步執行測試:
npx playwright test --debug
理解自動化測試流程
讓我們分析一下這個測試流程中的關鍵部分:
- 頁面導航:使用特定的選擇器定位和點擊元素,模擬用戶在網站上的導航。
- 等待機制:注意我們經常使用
waitFor()
方法確保元素完全載入後再進行操作。這是自動化測試的關鍵,因為網頁載入速度可能因網絡狀況而異。 - 滾動頁面:使用
scrollIntoViewIfNeeded()
確保元素在視口內,這模擬了用戶查看長頁面時的滾動行為。 - 強制點擊:有時需要使用
{ force: true }
選項來處理一些特殊情況,例如元素被其他元素部分遮擋時。 - 斷言(Assertions):使用
expect()
函數驗證操作結果,例如檢查選項是否被選中、產品是否成功加入購物袋。 - 錯誤處理:使用
try-catch
捕獲可能的錯誤,並提供有用的錯誤訊息。 - 截圖:測試結束時捕獲螢幕截圖,這對於調試非常有用。
這個測試示例展示了 Playwright 如何有效地自動化網頁交互流程,從產品選擇到加入購物車的整個過程。通過這種方式,你可以確保你的電子商務網站在各種瀏覽器和設備上都能正常運作。
小結與進階技巧
通過本教學,我們學習了如何:
- 使用頁面物件模式結構化測試代碼
- 在複雜網站上定位和操作元素
- 處理動態加載的頁面內容
- 驗證操作的結果
對於小型企業來說,這樣的自動化測試可以節省大量時間,並提高網站質量。即使是簡單的企業網站,也可以應用這些原則來測試關鍵流程,如聯繫表單、產品展示或預約功能。
進階提示
- 參數化測試: 嘗試使用不同的產品 規格(顏色、容量等)來測試同一流程
- 截圖比較: 使用 Playwright 的視覺比較功能來檢測 UI 變化
- 模擬不同設備: 配置測試以模擬手機、平板等不同設備上的使用體驗
- 持續集成: 將這些測試添加到你的 CI/CD 流程中,在每次代碼更改時自動執行
掌握了這些基礎之後,你已經準備好開始自動化測試你自己網站的關鍵功能了!
分享這篇文章: