Playwright测试代码重构:提高可维护性的技巧

简介: 本文分享Playwright测试代码的五大重构技巧:页面对象模型(POM)、配置常量化、智能等待、可复用步骤、数据驱动测试,并附实用建议与前后对比示例,助你从“能运行”迈向“易读、易改、易维护”。

当你接手一段两个月前写的Playwright测试代码,是不是常常要花上十分钟才能理清它在测什么?或者当页面元素稍作调整,你就得在十几个测试文件中逐个修改选择器?是时候聊聊重构了。

从“能运行”到“好维护”
让我们从一个常见的登录测试开始。这是很多人最初写的版本:

test('用户登录', async ({ page }) => {
await page.goto('https://app.example.com');
await page.fill('#username', 'testuser');
await page.fill('#password', 'password123');
await page.click('#login-btn');
await expect(page.locator('.welcome-msg')).toContainText('欢迎回来');
});
这段代码能工作,但问题很明显:URL、选择器、测试数据全部硬编码,改一处就要动全身。

技巧一:创建页面对象模型(POM)
把页面抽象成类,这是提升可维护性的第一步:

// pages/LoginPage.js
class LoginPage {
constructor(page) {
this.page = page;
this.usernameInput = page.locator('#username');
this.passwordInput = page.locator('#password');
this.loginButton = page.locator('#login-btn');
this.welcomeMessage = page.locator('.welcome-msg');
}

async navigate() {
awaitthis.page.goto('https://app.example.com/login');
}

async login(username, password) {
awaitthis.usernameInput.fill(username);
awaitthis.passwordInput.fill(password);
awaitthis.loginButton.click();
}
}

// 在测试中的使用
test('用户登录', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.navigate();
await loginPage.login('testuser', 'password123');
await expect(loginPage.welcomeMessage).toContainText('欢迎回来');
});
技巧二:提取配置和常量
那些散落在代码各处的字符串,早晚会给你带来麻烦:

// config/constants.js
exportconst URLs = {
LOGIN: process.env.BASE_URL + '/login',
DASHBOARD: process.env.BASE_URL + '/dashboard',
};

exportconst TestUsers = {
ADMIN: { username: 'admin_user', password: process.env.ADMIN_PASS },
STANDARD: { username: 'standard_user', password: 'test123' },
};
技巧三:实现智能等待策略
避免使用硬性的page.waitForTimeout(3000),那是脆弱的根源:

// utils/waitHelpers.js
exportasyncfunction waitForNetworkIdle(page, timeout = 10000) {
await page.waitForLoadState('networkidle', { timeout });
}

// 在页面对象中的使用
async submitForm() {
const responsePromise = this.page.waitForResponse('**/api/submit');
awaitthis.submitButton.click();
await responsePromise;
}
技巧四:创建可重用的测试步骤
那些频繁出现的操作序列,应该被封装起来:

// test-steps/loginSteps.js
exportasyncfunction loginAsUser(page, userType = 'STANDARD') {
const loginPage = new LoginPage(page);
const user = TestUsers[userType];

await loginPage.navigate();
await loginPage.login(user.username, user.password);
await expect(loginPage.welcomeMessage).toBeVisible();

returnnew DashboardPage(page); // 返回下一个页面对象
}
技巧五:实现数据驱动测试
当相似的测试用例只是数据不同时:

// test-data/loginData.js
exportconst loginTestData = [
{ userType: 'ADMIN', expectedRole: '管理员' },
{ userType: 'EDITOR', expectedRole: '编辑' },
{ userType: 'VIEWER', expectedRole: '查看者' },
];

// 测试文件
test.describe('不同角色登录', () => {
for (const data of loginTestData) {
test(${data.userType}用户登录后显示正确角色, async ({ page }) => {
const dashboard = await loginAsUser(page, data.userType);
await expect(dashboard.roleBadge).toContainText(data.expectedRole);
});
}
});

重构时的实用建议
小步前进:一次只重构一个功能模块,确保每个改动后测试都能通过
保持测试独立:每个测试都应该能独立运行,不依赖其他测试的状态
命名即文档:loginAsAdmin比loginTest1能传递更多信息
定期清理:每月花点时间回顾测试代码,删除不再需要的部分
一个重构后的完整示例
// 重构前
test('购物流程', async ({ page }) => {
// ... 长达50行的代码,混合了登录、搜索、加购、结账
});

// 重构后
test('完整购物流程', async ({ page }) => {
const dashboard = await loginAsUser(page, 'STANDARD');

const searchResults = await dashboard.searchProduct('Playwright实战指南');
await searchResults.selectFirstItem();

const productPage = new ProductPage(page);
await productPage.addToCart();

const cart = await productPage.goToCart();
await cart.proceedToCheckout();

const orderConfirmation = await cart.completePurchase();
await expect(orderConfirmation.successMessage).toBeVisible();
});
写在最后
好的测试代码不是一次写成的,而是不断重构的结果。刚开始时,让测试能跑起来更重要;但当测试规模扩大后,可维护性就成为团队效率的关键。

记住,你现在的重构不仅是为自己,也是为三个月后接手这段代码的同事——说不定就是你自己。

相关文章
|
2月前
|
人工智能 自然语言处理 测试技术
测试工程师的AI扫盲指南:一文搞懂人工智能核心术语
本文面向测试工程师,系统介绍AI核心概念(如ML、DL、LLM、CV、NLP等)、关键技术术语及实战应用(如视觉验证、日志异常识别、RAG、Prompt工程),并提供学习路径与工具实践建议,助力高效开展AI赋能的智能测试。
|
2月前
|
人工智能 架构师 安全
软件测试没有天花板,从“工具人”到“质量架构师”的破局之路
软件测试的瓶颈从不在于岗位,而在于能力边界与职业规划。18年老兵亲述:手工测试只是起点,真正的出路在于技术深耕、管理进阶或跨界转型。打破认知陷阱,拥抱AI与业务双轮驱动,用“T型能力”拓展职业天花板。你的未来,由每一次主动选择决定。
itextpdf 中文不显示问题
# 现象 itextpdf 打印时中文字体显示不出来,莫名其妙的消失不见了。具体现象如下图所示。 ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/ddc69588-4fc6-46ff-9d33-07f99340c963.png) 真正的理想情况如下图。 ![](https://ata2-img.oss-cn-zhangj
itextpdf 中文不显示问题
|
7天前
|
人工智能 测试技术
AI 写的测试用例,你敢直接用吗?这套判断方法,很多团队正在用
本文直击AI写测试用例的核心矛盾:不问“会不会写”,而聚焦“能不能用”。提出四大落地判断标准——业务贴合度、可执行性、异常覆盖力、规范一致性,帮测试工程师快速甄别AI用例价值,实现从“生成即用”到“工程化采纳”的跃升。
|
9天前
|
数据采集 人工智能 自然语言处理
别再给AI塞提示词了:Skill正在重塑Agent的能力边界
OpenClaw 的 Skill 体系代表 Agent 工程化新范式:不堆提示词,而是将 AI 能力拆解为可描述、可按需加载、可复用的单元。通过渐进式披露与三层加载机制,提升工具调用准确率与系统稳定性,让经验沉淀为可继承、可协作的工程资产。
|
7天前
|
人工智能 API 调度
Claude Skill 官方指南发布:能力模块化正在改变大模型工程结构
Anthropic发布30页《Skill创建指南》,首次系统定义大模型“能力模块化”范式。Skill介于Tool与Agent之间,强调职责单一、结构化I/O与可组合性,推动AI从提示工程迈向软件化能力架构。
|
8天前
|
人工智能 自然语言处理 IDE
Claude Code:把 AI 从“聊天工具”推进到可执行 Agent 系统
Claude Code 是Anthropic推出的本地化AI执行代理,非聊天工具,而是可驻留项目、读写文件、调用工具的Agent运行时。它以多Agent协作、MCP工具扩展和工程化生命周期管理为核心,将AI从“提供建议”升级为“参与执行”,重塑软件开发与个人工作流。
|
9天前
|
Web App开发 人工智能 安全
实测对比:ChatGPT写UI自动化脚本,到底靠不靠谱?
本文记录了作者用ChatGPT编写Selenium UI自动化脚本的真实实测:从登录、新增用户到多轮优化,对比AI生成代码与手写代码的差异。结果表明——AI能快速搭架子,但需人工引导、审查与加固;它不替代 tester,而是高效助手。
|
17天前
|
人工智能 监控 算法
除夕夜,不聊AI了,咱聊聊心里话
年夜饭刚吃完,春晚未启,亲戚追问将至。AI刷屏令人焦虑,“淘汰论”四起。但今夜不聊学习、不谈技术,只问一句:若AI冲击岗位,你最该想明白的是什么?答案不在工具,而在“你想成为谁”。
|
8天前
|
人工智能 移动开发 安全
那个会自己写测试用例的AI,今天把我逼到了墙角
一场用例评审会,测试工程师的17条用例 vs AI生成的43条——覆盖更全、维度更广、耗时仅43秒。震撼之余,他发现AI无经验盲区,而人有判断力与历史洞察。二者不是替代,而是互补:AI拓广度,人守深度。被逼到墙角,他选择翻越。

热门文章

最新文章