Skip to content

Playwright 页面对象模型(POM)

本章介绍页面对象模型(Page Object Model)的设计思想和实现方式,帮助你组织大型测试套件。


什么是 POM

页面对象模型是一种测试设计模式,将页面的元素和操作封装到独立的类中。

每个页面对象代表 Web 应用中的一个页面或组件,包含该页面的定位器和操作方法。


POM 的优势

优势说明
简化测试编写测试代码使用高层 API(如 loginPage.login('user', 'pass'))而非原始的 Locator 操作
降低维护成本页面结构的变更只需要修改 Page Object 类,不需要修改每个测试
提高可读性测试代码更像业务语言,非技术人员也能理解
避免重复相同的定位器和操作只需定义一次

创建 Page Object 类

下面以 RUNOOB 网站为例,创建一个登录页面的 Page Object。

实例

bash

// 文件路径:pages/LoginPage.ts

import { type Page, type Locator, expect } 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('用户名');

    this.passwordInput = page.getByLabel('密码');

    this.loginButton = page.getByRole('button', { name: '登录' });

    this.errorMessage = page.getByTestId('login-error');

  }

  // 导航到登录页

  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 expectLoginError(message: string) {

    await expect(this.errorMessage).toBeVisible();

    await expect(this.errorMessage).toContainText(message);

  }

}

在测试中使用 Page Object

实例

javascript

// 文件路径:tests/login.spec.ts

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

import { LoginPage } from '../pages/LoginPage';

test.describe('登录功能', () => {

  test('正确用户名密码登录成功', async ({ page }) => {

    const loginPage = new LoginPage(page);

    // 使用 Page Object 的方法

    await loginPage.goto();

    await loginPage.login('runoob_user', 'correct_password');

    // 登录成功后验证跳转

    await expect(page).toHaveURL('/dashboard');

  });

  test('错误密码登录失败', async ({ page }) => {

    const loginPage = new LoginPage(page);

    await loginPage.goto();

    await loginPage.login('runoob_user', 'wrong_password');

    // 使用 Page Object 的断言方法

    await loginPage.expectLoginError('用户名或密码错误');

  });

});

多层页面对象

在真实项目中,可以创建多个 Page Object,它们之间可以相互关联。

实例

bash

// 文件路径:pages/DashboardPage.ts

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

export class DashboardPage {

  readonly page: Page;

  readonly welcomeText: Locator;

  readonly logoutButton: Locator;

  constructor(page: Page) {

    this.page = page;

    this.welcomeText = page.getByText('欢迎回来');

    this.logoutButton = page.getByRole('button', { name: '退出' });

  }

  async expectWelcomeMessage(username: string) {

    await expect(this.welcomeText).toContainText(username);

  }

  async logout() {

    await this.logoutButton.click();

  }

}

页面对象的组合使用:

实例

javascript

test('完整的登录登出流程', async ({ page }) => {

  const loginPage = new LoginPage(page);

  const dashboardPage = new DashboardPage(page);

  // 登录

  await loginPage.goto();

  await loginPage.login('runoob_user', 'password');

  // 验证仪表盘

  await dashboardPage.expectWelcomeMessage('runoob_user');

  // 登出

  await dashboardPage.logout();

  await expect(page).toHaveURL('/login');

});

POM 最佳实践

实践说明
Locator 集中在构造函数所有定位器在构造函数中定义,不要在方法中动态创建
方法返回 Page Object操作方法可以返回新的 Page Object(如登录后返回 DashboardPage)
断言封装但不滥用常用的断言组合封装为方法,简单的断言直接在测试中写
不用继承用组合避免深层继承,使用组合模式关联多个 Page Object
一套对象覆盖一个页面/组件粒度适中,不要为每个小元素创建单独的类

POM 不是强制要求。

对于只有几个测试的小项目,直接在测试中写 Locator 也更清晰。当测试文件超过 5 个以上时,考虑引入 POM。

AI 思考中...

Playwright Frame 与新窗口

Playwright Fixtures 与参数化

基于 VitePress 构建,部署于 GitHub Pages