Playwright 架构UI 自动化质量保障平台

我最近花了两天时间深入学习了一个基于 Playwright 和 Pytest 的 Web UI 自动化测试框架。这个项目不仅让我对自动化测试有了全新的认识,更让我体会到了企业级测试框架的设计理念。

今天,我想通过这篇博客分享我的学习心得,包括项目的架构设计、技术选型、核心实现,以及我个人的学习过程。

项目概述

什么是 Playwright-UI?

这是一个企业级的 Web UI 自动化测试框架,专门用于测试 Web 应用的各种功能模块。项目采用现代化的技术栈,实现了完整的测试流程:从用例编写到报告生成。

核心特性:

基于 Playwright 的浏览器自动化

采用 Page Object Model (POM) 设计模式

支持网络请求拦截和 Mock API

自动生成美观的 Allure 测试报告

完整的调试和追踪功能

项目架构深度剖析

整体架构图

playwright-ui/

├── cases/ # 测试用例集

├── pages/ # 页面对象层 (POM)

├── mocks/ # Mock API 数据

├── plugins/ # 自定义插件

├── reports/ # 测试报告临时文件

├── allure_report/ # 生成的 Allure 报告

├── test-results/ # 截图、视频、追踪日志

├── conftest.py # Pytest 全局配置

├── pytest.ini # Pytest 参数配置

├── run.py # 框架运行入口

└── requirements.txt # 项目依赖

核心目录详解

cases/ - 测试用例集

这是项目的心脏,存放所有测试用例。每个功能模块都有对应的测试类:

cases/

├── test_login.py # 登录功能测试

├── test_register.py # 注册功能测试

├── test_project_list.py # 项目列表测试

├── test_add_project.py # 新增项目测试

├── test_add_module.py # 新增模块测试

├── test_env_list.py # 环境列表测试

└── conftest.py # 用例级配置

设计亮点:

按功能模块组织测试类

使用 Pytest 的 class 结构

每个测试方法都有清晰的文档字符串

pages/ - 页面对象层 (POM 模式)

这是项目的灵魂,实现了经典的 Page Object Model 设计模式:

pages/

├── login_page.py # 登录页面封装

├── register_page.py # 注册页面封装

├── project_list_page.py # 项目列表页面

├── add_project_page.py # 新增项目页面

├── add_module_page.py # 新增模块页面

└── list_env_page.py # 环境列表页面

POM 模式的核心思想:

将页面元素定位和业务操作分离

提高代码的可维护性和复用性

当页面 UI 变化时,只需修改 Page 类

mocks/ - Mock API 数据

用于模拟各种异常场景的 API 响应:

mocks/

└── mock_api.py # Mock API 定义

Mock 的作用:

测试异常场景(400、500 错误)

模拟后端未开发完成的情况

提高测试的稳定性和速度

配置文件详解

pytest.ini - Pytest 参数配置:

pytest

addopts = -p no:playwright # 禁用默认插件

-p no:base_url # 禁用基础 URL 插件

--headed # 有界面模式

--tracing=retain-on-failure # 失败时保留追踪

--screenshot=only-on-failure # 失败时截图

--video=retain-on-failure # 失败时录视频

--base-url=http://47.116.12.183 # 测试服务器

conftest.py - 全局配置:

注册自定义插件

配置浏览器参数

设置 Allure 报告的动态标题

💻 核心技术实现

Playwright 浏览器自动化

项目使用 Playwright 进行浏览器操作,支持多种定位方式:

元素定位

page.get_by_label("用户名:") # 按标签定位

page.get_by_text("立即登录") # 按文本定位

page.locator('[data-testid="btn"]') # CSS 选择器

元素交互

element.fill("输入内容") # 填充输入框

element.click() # 点击操作

element.select_option("选项值") # 选择下拉框

Page Object Model 实现

以 LoginPage 为例:

class LoginPage:

def init (self, page: Page):

self.page = page

元素定位器

self.locator_username = page.get_by_label("用 户 名:")

self.locator_password = page.get_by_label("密 码:")

self.locator_login_btn = page.locator('text=立即登录')

复制代码
    # 错误提示元素
    self.locator_username_tip1 = page.locator('[data-fv-validator="notEmpty"]')
    self.locator_login_error = page.locator('text=账号或密码不正确!')

def navigate(self):
    self.page.goto("/login.html")

def fill_username(self, username):
    self.locator_username.fill(username)

def fill_password(self, password):
    self.locator_password.fill(password)

def click_login_button(self):
    self.locator_login_btn.click()

def login(self, username, password):
    """完整登录流程"""
    self.fill_username(username)
    self.fill_password(password)
    self.click_login_button()

网络请求拦截

Playwright 支持拦截和验证网络请求:

验证 API 请求内容

with page.expect_request('**/api/login') as req:

login_button.click()

assert req.value.method == 'POST'

assert req.value.post_data_json == {

'username': 'testuser',

'password': 'testpass'

}

验证 API 响应

with page.expect_response('**/api/login') as res:

login_button.click()

assert res.value.status == 200

assert res.value.ok

Mock API 实现

通过 Mock 模拟各种异常场景:

模拟项目名重复的错误

mock_project_400 = {

"url": "**/api/project",

"handler": lambda route: route.fulfill(

status=400,

body=json.dumps({

"errors": {"project_name": "项目名已存在"},

"message": "Input payload validation failed"

})

)

}

模拟服务器错误

mock_project_500 = {

"url": "**/api/project",

"handler": lambda route: route.fulfill(

status=500,

body="服务端错误"

)

}

📝 测试用例编写

基本测试结构

class TestLogin:

"""登录功能测试"""

复制代码
@pytest.fixture(autouse=True)
def start_for_each(self, unlogin_page: Page):
    """前置操作:初始化页面对象"""
    self.login = LoginPage(unlogin_page)
    self.login.navigate()
    yield
    # 后置操作(如果需要)

def test_login_success(self):
    """正常登录流程"""
    self.login.fill_username("py")
    self.login.fill_password("123456")
    self.login.click_login_button()
    
    # 断言验证
    expect(self.login.page).to_have_title("首页")
    expect(self.login.page).to_have_url("/index.html")

def test_login_empty_username(self):
    """用户名为空验证"""
    self.login.fill_username("")
    self.login.fill_password("123456")
    self.login.click_login_button()
    
    # 验证错误提示
    expect(self.login.locator_username_tip1).to_be_visible()
    expect(self.login.locator_username_tip1).to_contain_text("不能为空")

@pytest.mark.parametrize("username,password", [
    ["py", "wrongpass"],
    ["wronguser", "123456"],
])
def test_login_error(self, username, password):
    """参数化测试:错误账号密码"""
    self.login.fill_username(username)
    self.login.fill_password(password)
    self.login.click_login_button()
    
    expect(self.login.locator_login_error).to_be_visible()

测试场景覆盖

项目测试了完整的用户旅程:

表单验证(必填、空值、格式等)

正常业务流程

异常错误处理

API 请求验证

页面导航验证

测试报告和调试

Allure 报告

项目使用 Allure 生成美观的测试报告:

运行测试:

python run.py

生成报告:

allure generate ./reports -o ./allure_report --clean

查看报告:

allure serve ./reports

或直接打开 allure_report/index.html

报告包含:

测试用例执行结果统计

每个用例的详细执行时间

失败用例的截图和视频

测试步骤的详细日志

调试功能

Playwright 提供了强大的调试工具:

1. 截图调试

--screenshot=only-on-failure # 失败时自动截图

2. 视频录制

--video=retain-on-failure # 失败时录制视频

3. 追踪日志

--tracing=retain-on-failure # 失败时记录追踪

查看追踪日志

playwright show-trace test-results/trace.zip

测试报告截图

服务器有点问题,很多测试没过

绿色 (13):通过(Passed)。

红色 (6):Product defects(产品缺陷)。通常指断言失败,即程序运行了,但结果不对。

黄色 (38):Test defects(测试脚本缺陷/环境问题)。环境不通挂掉。

相关推荐
fire-flyer2 小时前
ClickHouse系列(六):Kafka 到 ClickHouse 的生产级写入架构
clickhouse·架构·kafka
Bohemian—Rhapsody2 小时前
麒麟v10-arm架构部署rabbitmq
arm开发·架构·rabbitmq
2603_954708312 小时前
微电网主从控制架构:集中式调度与分布式执行的协同机制
人工智能·分布式·物联网·架构·系统架构·能源
小猿姐5 小时前
# KubeBlocks for MSSQL 高可用实现
数据库·架构·sql server
古译汉书10 小时前
【IoT死磕系列】Day 9:架构一台“自动驾驶物流车”,看8种协议如何协同作战
网络·arm开发·单片机·物联网·tcp/ip·架构·自动驾驶
KaneLogger11 小时前
从传统笔记到 LLM 驱动的结构化 Wiki
人工智能·程序员·架构
斯外戈的小白11 小时前
【Agent】LangChain 1.0架构
架构·langchain
小橘子83111 小时前
(学习)Claude Code 源码架构深度解析
学习·程序人生·架构
C'ᴇsᴛ.小琳 ℡13 小时前
架构技术演进的方向
架构