Previous
Day 13 · Fixtures
Next
Day 15 · Weekly Review 3: Project Structure Refactor
项目结构
Day 1460-120 minutesPlaywright QA hands-on drill

Day 14 - Page Object Model

今日目标

  • 掌握 POM 的基本思想。
  • 学会把页面操作封装成类。

学习时间安排(60–120 分钟)

时间模块做什么
0-20 分钟核心概念与词汇读概念表,重点理解封装边界和命名语义。
20-45 分钟官方文档阅读读 Page object models 文档,对照官方示例。
45-75 分钟实操练习为 HomePage 和 LoginPage 创建 POM。
75-105 分钟示例代码改写把现有测试里的 locator 迁移进 Page Object。
105-120 分钟复盘与作业完成今日问题,检查是否过度封装。

核心概念与词汇

English中文场景用法
Page Object Model (POM)页面对象模型Use it when you encapsulate a page's locators and actions into a class.
encapsulation封装Use it when you hide locators inside page classes so tests stay readable.
locator property定位器属性Use it when you declare readonly locators on a page class.
behavior method行为方法Use it when a method expresses a user action like login() or search().
assertion method断言方法Use it when a method expresses a page state like expectLoaded().
goto()页面跳转方法Use it when the page object navigates to its own URL.
DRY不重复原则Use it when the same locator would otherwise appear in many tests.
maintainability可维护性Use it when UI changes should only touch one class, not many tests.
over-engineering过度封装Use it when a page class hides too much and becomes hard to understand.
reusable component可复用组件Use it when a header, footer, or widget is shared across pages.

学习材料

  • 必读:[Page object models](https://playwright.dev/docs/pom)
  • 必读:[Best practices](https://playwright.dev/docs/best-practices) 中关于 POM 的说明
  • 选读:[Playwright 官方 TodoMVC 示例项目](https://github.com/microsoft/playwright/tree/main/examples) 里的 POM 用法

重点理解

Page Object 示例:

import { expect, Page, Locator } from '@playwright/test';

export class HomePage {
  readonly page: Page;
  readonly getStartedLink: Locator;

  constructor(page: Page) {
    this.page = page;
    this.getStartedLink = page.getByRole('link', { name: 'Get started' });
  }

  async goto() {
    await this.page.goto('/');
  }

  async openIntro() {
    await this.getStartedLink.click();
  }

  async expectLoaded() {
    await expect(this.page).toHaveTitle(/Playwright/);
  }
}

测试中使用:

test('open intro', async ({ page }) => {
  const homePage = new HomePage(page);
  await homePage.goto();
  await homePage.expectLoaded();
  await homePage.openIntro();
});

实操步骤

为一个页面创建 POM:

pages/HomePage.ts
pages/LoginPage.ts

要求:

  • locator 放在 class 中。
  • 行为方法表达业务动作。
  • 断言方法表达页面状态。
  • 测试用例不要出现大量底层 locator。

示例代码

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

export class LoginPage {
  readonly page: Page;
  readonly usernameInput: Locator;
  readonly passwordInput: Locator;
  readonly loginButton: Locator;
  readonly errorMessage: Locator;

  constructor(page: Page) {
    this.page = page;
    this.usernameInput = page.getByLabel('Username');
    this.passwordInput = page.getByLabel('Password');
    this.loginButton = page.getByRole('button', { name: 'Login' });
    this.errorMessage = page.getByText('Invalid username or password');
  }

  async goto() {
    await this.page.goto('/login');
  }

  async login(username: string, password: string) {
    await this.usernameInput.fill(username);
    await this.passwordInput.fill(password);
    await this.loginButton.click();
  }

  async expectLoginFailed() {
    await expect(this.errorMessage).toBeVisible();
  }
}

常见坑

  • 方法名叫 clickButton() 而不是 login(),调用方看不懂业务。
  • locator 仍然散落在测试里,POM 形同虚设。
  • 一个 Page 类几百行,什么组件都塞进去,没人敢改。
  • 断言全塞进 POM,测试里看不到验证了什么。
  • 为了“完全复用”强行继承/抽象,最后页面类之间耦合在一起。

今日产出

  • 至少 1 个 Page Object。
  • 至少 2 条使用 POM 的测试。

今日问题

  1. POM 解决什么维护问题?
  2. Locator 应该放在测试里还是 Page Object 里?
  3. Page Object 方法应该叫 clickButton() 还是 login()
  4. POM 会不会过度封装?
  5. 断言应该放在 POM 里还是测试里?

复盘要点

  • POM 的验收标准:页面改版时,90% 的修改只发生在 page 类里。
  • 方法名用业务语言(login、search、checkout),测试读起来像操作手册。
  • 通用断言放 POM(expectLoaded),业务断言留测试(验证具体结果),保持平衡。

AI 时代扩展:AI 辅助 Playwright 测试

新增概念

English中文
POM generationPOM 生成
component abstraction组件抽象
naming convention命名规范

适用场景

让 AI 根据页面描述或测试代码反推 POM 骨架,或者审查现有 POM 的职责划分和命名。注意 AI 生成的 locator 必须经过真实页面验证。

可复用表达 / 提示词

我有一个登录页,包含用户名、密码、登录按钮和错误提示,请生成 LoginPage 类骨架,方法使用业务语义命名。
请审查我的 HomePage,指出过度封装、命名不当和职责越界的地方。

追问加练

  • AI 生成的 POM 把所有断言都封装进去了,你怎么调整?
  • 组件复用(页头、页脚)和页面类应该怎么组织?
  • AI 怎么帮你判断一个方法该叫 clickButton 还是 login?

今日作业

  • 完成 HomePage 和 LoginPage,把登录流程测试迁移到 POM 上跑通。
  • 写一页笔记:POM 的 5 条设计原则(用今天的坑反推)。
  • 让 AI 审查你的 POM 命名,统一方法命名风格。

自检清单

Previous
Day 13 · Fixtures
Next
Day 15 · Weekly Review 3: Project Structure Refactor