页面对象与功能辅助函数
前段时间,我发布了使用Cypress的函数式编程测试模式,我在那篇文章中深入探讨了为什么在现代测试自动化中页面对象不再必要。当时,我并没有意识到那个看法是多么超前于当时的潮流——因为至今,仍然有很多人仍然坚持使用页面对象。
这篇帖子是对该论点的一个更简单、更新的版本。
此处省略内容
🚀 函数助手比页面对象更好
页面对象模型 (POM) 遵循继承,而功能辅助函数则采用组合模式。
但现代 web 应用是用 基于组件的架构 构建的,组件才是真正的构建模块,而不是页面。
❓ 组件组合而成,页面不过是由这些组件构成的,那么用类来抽象页面真的有必要吗? 还是说这会不会导致不必要的重复和过度抽象呢?
在现代测试框架,例如 Playwright 和 Cypress 中,严格的 页面对象模式(Page Object Model) 往往显得过于繁琐,特别是在以下情况时:
✅ 你正在使用数据选择器(data-qa
,data-cy
)用于创建稳定的定位器。
✅ 工具本身已经提供了强大的内置工具用于 UI 交互。
✅ POM 引入了额外的复杂性,使得调试更复杂。
……
为什么页面对象模式不再适用?1️⃣ 多余的抽象化
- POM 添加了一层,这通常提供不了多少实际帮助。
- 现代测试框架本身已经非常强大,不需要这个额外的层。
页面继承是冗余设计
备注:由于中文中没有直接对应的emoji风格数字,这里保持了英文原文的格式。如果有特定需求可以调整为纯中文数字“二”。
- 仅仅为了封装Playwright(或Cypress)API的
BasePage
类是没有意义的。 - Playwright(或Cypress)已经提供了诸如
page.locator()
、page.click()
、page.fill()
等方法。
3️⃣ 调试难题
- 使用 POM 时,如果测试失败,你 不得不在多个文件间来回切换 来找出问题所在。
-
使用 直接帮助函数 ,你 一目了然。
-
- *
🚨 POM存在的问题:
❌ 不必要的复杂性 → 额外的类和继承
❌ 更难排查问题 → 需要在不同文件间切换
❌ 毫无必要地包装Playwright的API
🔹 示例 (LoginPage.js
- POM)
class LoginPage {
// 登录页面
constructor(page) {
this.page = page;
// 用户名字段
this.usernameField = page.locator('[data-testid="username"]');
// 密码字段
this.passwordField = page.locator('[data-testid="password"]');
// 登录按钮
this.loginButton = page.locator('[data-testid="login-button"]');
}
// 登录方法
async login(username, password) {
await this.usernameField.fill(username);
await this.passwordField.fill(password);
await this.loginButton.click();
}
}
export default LoginPage;
全屏 退出
🔹 测试中的使用
import { test, expect } from '@playwright/test';
import LoginPage from './LoginPage.js';
test('用户可以登录', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.login('testUser', 'password123');
await expect(page.locator('[data-testid="welcome-message"]')).toHaveText('Welcome, testUser');
});
按一下全屏,再按一下退出全屏
✅ 功能性辅助方法(更佳)
📌 为什么这更好?
✅ 不需要额外的类 → 直接使用 Playwright API
✅ 无需不必要的 this.page
赋值
✅ 更容易维护和调试
🔹 示例(例如) (loginHelpers.js
- 功能辅助函数)
export async function login(page, username, 密码) {
await page.fill('[data-testid="username"]', username);
await page.fill('[data-testid="password"]', 密码);
await page.click('[data-testid="login-button"]');
}
全屏 退出全屏
🔹 测试中的用法
import { test, expect } from '@playwright/test';
import { login } from './loginHelpers.js';
test('用户可以登录成功', async ({ page }) => {
await login(page, 'testUser', 'password123');
await expect(page.locator('[data-testid="welcome-message"]')).toHaveText('欢迎,测试用户');
});
点击此处切换全屏模式
最后的想法
辅助函数更简单明了,更容易追踪并解决错误,且在组件驱动的应用中更容易扩展和适应。
💡 POM 在 Selenium/WebDriver 时代很有用,但是现在直接用函数就好了。
🔥 你觉得呢? 你还在使用 POM 吗?POM 是指某某方法或概念,你已经切换到使用函数工具了吗?
💬 下面留言吧 — 我很想知道你的看法!