Playwright 自动化测试系列(7)| 第三阶段:测试框架集成​​Page Object 模式

130 阅读4分钟

一、Page Object 模式的核心价值

1. 问题驱动
传统测试脚本将元素定位与操作逻辑混写在用例中,导致:

  • 维护灾难:页面元素变动需修改所有相关用例(如登录按钮ID变更需全局搜索替换)

  • 复用困难:相同操作逻辑无法跨用例共享(如登录流程重复编写)

  • 可读性差:业务逻辑被技术细节淹没(如page.locator("#login-btn").click()分散在多个步骤)

2. 解决方案
Page Object(PO)模式将页面抽象为类

  • 元素定位器 → 封装为类属性

  • 操作行为 → 封装为类方法

  • 业务流 → 通过方法组合实现

    传统模式 vs PO模式对比

    传统:元素定位与操作耦合

    page.locator("#username").fill("user1")
    page.locator("#password").fill("pass1")
    page.locator("#login-btn").click() # PO模式:业务语义化
    login_page.enter_username("user1")
    login_page.enter_password("pass1")
    login_page.click_login()

二、电商全流程PO架构设计

1. 系统分层架构

2. 核心类设计

类名

职责

关键方法举例

LoginPage

登录页面操作封装

enter_credentials()

, submit()

HomePage

首页导航与搜索

search_product()

, view_cart()

ProductPage

商品详情操作

select_color()

, add_to_cart()

CartPage

购物车管理

apply_coupon()

, checkout()

OrderPage

订单结算流程

fill_address()

, confirm_pay()

三、实战:电商登录-下单PO封装

1. 登录页封装(LoginPage)
class LoginPage:      
def __init__(self, page):        
self.page = page          

self.username_input = page.get_by_placeholder("手机号/邮箱")  
# 使用面向用户定位器          
self.password_input = page.get_by_label("密码")          
self.submit_btn = page.get_by_role("button", name="登录")      
def navigate_to_login(self):        
self.page.goto("https://mall.example.com/login")      
def enter_credentials(self, username, password):        
self.username_input.fill(username)          
self.password_input.fill(password)      
def submit(self):        
self.submit_btn.click()          
self.page.wait_for_url("**/dashboard")  # 显式等待导航完成  
2. 购物车页封装(CartPage)
class CartPage:      
def __init__(self, page):        
self.page = page          
self.coupon_field = page.locator("[data-testid='coupon-input']")  # 测试ID定位增强稳定性          self.apply_btn = page.get_by_text("应用优惠券")      def apply_coupon(self, code: str):        self.coupon_field.fill(code)          self.apply_btn.click()          expect(self.page.get_by_text("优惠券已生效")).to_be_visible()  # 自动等待 + 断言      def checkout(self):        self.page.get_by_role("button", name="结算").click()  
3. 业务流组合(测试用例层)
def test_e2e_order(login_page, home_page, cart_page):      
# 登录 → 搜索 → 加购 → 结算      
login_page.navigate_to_login()      
login_page.enter_credentials("user1", "securePass123")      
login_page.submit()      
home_page.search_product("无线耳机")      
home_page.select_first_product()      
product_page.add_to_cart(quantity=2)      
cart_page.apply_coupon("FESTIVAL2025")      
cart_page.checkout()      
order_page.confirm_payment()      
assert order_page.get_order_status() == "支付成功"  

四、PO模式高级技巧

1. 复合操作封装

避免原子操作暴露,直接提供业务接口:

# 反例:调用方需知道操作细节  
login_page.enter_username("user1")  
login_page.enter_password("pass1")  
login_page.click_login()  # 正例:业务语义封装  
login_page.login_with("user1", "pass1")  
2. 动态元素处理

使用Playwright的Locator API处理动态生成元素:

def get_product_element(self, name):      
return self.page.locator(f".product-item:has-text('{name}')")  # 文本+CSS组合定位  
3. 多层级PO复用

通过页面嵌套实现复杂组件复用:

class HeaderComponent:      
def __init__(self, page):        
self.search_bar = page.get_by_role("searchbox")      
def search(self, keyword):        
self.search_bar.fill(keyword)          
self.search_bar.press("Enter")  
class HomePage:    
def __init__(self, page):        
self.page = page          
self.header = HeaderComponent(page)  # 复用头部组件  

五、Pytest集成与多用户测试

1. 测试隔离设计

利用Playwright的browser.new_context()实现用户会话隔离:

# conftest.py  
@pytest.fixture(scope="function")  
def user_context(playwright):      
browser = playwright.chromium.launch()      
context = browser.new_context(storage_state="user1_session.json")  # 复用登录态      
yield context      
context.close()  
2. 多用户并发测试

模拟用户并发抢购场景:

def test_concurrent_purchase(user_context_factory):      
with ThreadPoolExecutor() as executor:          # 启动3个并发用户          
futures = [              
executor.submit(run_user_flow, user_context_factory("user1")),              
executor.submit(run_user_flow, user_context_factory("user2")),              
executor.submit(run_user_flow, user_context_factory("user3"))          ]          
results = [f.result() for f in futures]      
assert all("下单成功" in r for r in results)  

六、常见问题解决方案

问题场景

根因

PO模式解决方案

元素定位失效

页面结构调整或属性变更

集中维护定位器,修改仅影响PO类

异步加载导致操作失败

元素未就绪

PO方法内封装wait_for_selector

跨页面依赖管理复杂

页面跳转状态传递丢失

返回新页面对象(return OrderPage(self.page)

多语言版本兼容性差

文本定位器硬编码

抽取文本资源到独立配置文件

七、总结:PO模式的核心收益

  1. 可维护性:页面变更只需修改对应PO类,不影响200+测试用例

  2. 可读性:用例清晰表达业务意图(login_page.login_with() vs 5行定位代码)

  3. 复用性:购物车操作复用率达90%(加购/改数量/结算等)

  4. 扩展性:通过组件化设计支持新页面快速接入

终极实践建议

  • 原子操作 → 封装在PO方法内

  • 业务流程 → 组合PO方法实现

  • 验证逻辑 → 通过expect断言集成

  • 环境隔离 → 善用browser.new_context()

通过本实战,可构建日均执行5000+用例的电商测试框架,缺陷捕捉率提升40%,维护成本降低70%。

Playwright学习交流群

推荐阅读

Playwright 自动化测试系列(6)| 第三阶段:测试框架集成​指南:参数化测试 + 多浏览器并行执行
Playwright自动化测试系列课(5) | ​​调试神器实战:Trace Viewer 录屏分析 + AI 辅助定位修复​Playwright自动化测试系列课(4) | 异步加载克星:自动等待 vs 智能等待策略深度解析Playwright自动化测试系列课(3) | 第二阶段:核心技能与调试 ​​交互操作大全
Playwright系列课(2) | 元素定位四大法宝:CSS/文本/XPath/语义化定位实战指南
Agent的深度解析:从原理到实践
Playwright 极速入门:1 小时搞定环境搭建与首个测试脚本
Kimi K2开源炸场,1万亿参数碾压GPT-4.1,成本仅Claude 4的1/5!
10+热门 AI Agent 框架深度解析:谁更适合你的项目?
从零开始学 Dify:搭建你的第一个 LLM 应用平台
10大开源MCP神器!让你的AI从菜鸟变超人