基于目标 Web 项目真实落地经验编写的实战教程,涵盖从入门到企业级框架搭建的完整路径。
目录
- [第一篇:Playwright 介绍与试用](#第一篇:Playwright 介绍与试用)
- [第二篇:快速上手 --- 一条测试用例的核心组成](#第二篇:快速上手 — 一条测试用例的核心组成)
- 第三篇:落地项目实战
- 附录
第一篇:Playwright 介绍与使用
1.1 什么是 Playwright
核心定位 :Playwright Test 是端到端(E2E)Web 应用自动化测试框架,支持在 Windows、Linux 和 macOS 上运行 Chromium、WebKit 和 Firefox,无论是本地还是 CI 环境,无论是无头模式还是有头模式,并提供 Chrome (Android) 和 Mobile Safari 的原生移动仿真。
支持语言 :Python、JavaScript/TypeScript、Java、.NET C#
1.2 与 Selenium / Cypress 对比
| 维度 | Playwright | Selenium | Cypress |
|---|---|---|---|
| 架构 | 直接通过浏览器协议通信 | 通过 WebDriver 中间层 | 浏览器内嵌执行 |
| 浏览器支持 | Chromium / Firefox / WebKit | 全部主流浏览器 | 仅 Chromium |
| 语言支持 | Python / JS / Java / C# | Java / Python / C# / Ruby 等 | 仅 JavaScript |
| 自动等待 | 内置,所有操作自动等待 | 需手动 WebDriverWait |
内置 |
| 并行执行 | 原生支持 | 需 TestNG / pytest-xdist | 有限支持 |
| 执行速度 | 快(无中间层) | 中(WebDriver 开销) | 中(浏览器内执行) |
| 网络拦截 | 原生支持 | 需额外代理 | 原生支持 |
| 社区生态 | 成长中(Microsoft 维护) | 最成熟 | 活跃但局限于 JS |
选型建议:
- 新项目、需跨浏览器、Python 技术栈 → Playwright
- 已有 Selenium 资产、需支持 Safari IE → Selenium
- 纯前端团队、仅测 Chromium → Cypress
1.3 环境准备与安装
系统要求
- Python ≥ 3.8
- Windows / macOS / Linux
安装步骤
bash
# 1. 创建虚拟环境
python -m venv .venv
# Windows 激活
.venv\Scripts\activate
# macOS/Linux 激活
source .venv/bin/activate
# 2. 安装 Playwright 及 pytest 插件
pip install playwright pytest-playwright pytest-html
# 3. 安装浏览器二进制文件(关键步骤)
python -m playwright install
# 安装完成后会下载三个浏览器:
# - Chromium (~150MB)
# - Firefox (~100MB)
# - WebKit (~100MB)
验证安装
bash
python -c "from playwright.sync_api import sync_playwright; print('OK')"
1.4 第一个 Playwright 脚本
python
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://www.example.com")
print(f"页面标题: {page.title()}")
page.screenshot(path="first_screenshot.png")
browser.close()
录制工具(codegen)
bash
# 启动录制工具,自动生成代码
python -m playwright codegen https://www.example.com
录制时浏览器打开目标页面,你在页面上的所有操作会被实时转化为 Python 代码。适合快速了解页面元素的选择器和操作流程。
知识点小结
| 概念 | 说明 |
|---|---|
sync_playwright() |
同步上下文管理器,返回 Playwright 实例 |
p.chromium.launch() |
启动浏览器,headless=False 显示界面 |
browser.new_page() |
创建新标签页 |
page.goto() |
导航到指定 URL |
page.screenshot() |
截图,full_page=True 截取完整页面 |
第二篇:快速上手 --- 一条测试用例的核心组成
2.1 核心组成:四个角色
一条完整的 UI 自动化测试用例,无论项目多复杂,本质上由四个角色协作完成:
┌─────────────────────────────────────────────────────┐
│ 测试用例 (Test) │
│ · 描述"验证什么",不关心"怎么操作" │
│ · 包含步骤编排 + 断言 │
└───────────────┬─────────────────────────────────────┘
│ 调用
▼
┌─────────────────────────────────────────────────────┐
│ 页面对象 (Page Object) │
│ · 描述"怎么操作",封装元素定位与业务动作 │
│ · 继承自 BasePage,复用通用操作原语 │
└───────────────┬─────────────────────────────────────┘
│ 继承
▼
┌─────────────────────────────────────────────────────┐
│ 基础页面类 (BasePage) │
│ · 封装 Playwright 底层 API:click / fill / wait 等 │
│ · 所有页面对象共享同一套操作原语 │
└───────────────┬─────────────────────────────────────┘
│ 底层调用
▼
┌─────────────────────────────────────────────────────┐
│ Playwright Page (浏览器页面) │
│ · 由 pytest-playwright 插件自动注入 │
│ · 真实操作浏览器,执行 DOM 交互 │
└─────────────────────────────────────────────────────┘
一句话总结:测试用例调页面对象 → 页面对象继承基础页面类 → 基础页面类调 Playwright Page → Playwright 操作浏览器。
2.2 四个角色的职责边界
| 角色 | 关注什么 | 不关心什么 |
|---|---|---|
| 测试用例 | 验证什么(断言)、步骤顺序 | 元素怎么定位、按钮怎么点 |
| 页面对象 | 元素定位器、业务动作组合 | 断言逻辑、测试数据来源 |
| 基础页面类 | Playwright API 封装、等待策略 | 具体页面的业务逻辑 |
| Playwright Page | 浏览器协议通信、DOM 操作 | 测试框架、业务逻辑 |
设计原则:上层只依赖下层接口,不跨层调用。测试用例不直接操作 Playwright Page,页面对象不包含断言。
2.3 最小骨架示例
用最少的代码展示四个角色如何串联:
python
# === 角色三:BasePage(只贴核心方法,省略完整实现) ===
class BasePage:
def __init__(self, page):
self.page = page
self.url = ""
def navigate(self):
self.page.goto(self.url)
def click(self, selector):
self.page.locator(selector).click()
def fill(self, selector, text):
self.page.locator(selector).fill(text)
def is_visible(self, selector):
return self.page.locator(selector).is_visible()
# === 角色二:Page Object ===
class LoginPage(BasePage):
def __init__(self, page):
super().__init__(page)
self.url = "https://example.com/login"
self.email_input = "input[name='email']"
self.password_input = "input[name='password']"
self.submit_btn = "button[type='submit']"
def login(self, email, password):
self.fill(self.email_input, email)
self.fill(self.password_input, password)
self.click(self.submit_btn)
# === 角色一:Test Case ===
def test_login_success(page): # ← 角色四:page 由 pytest-playwright 自动注入
login_page = LoginPage(page) # 1. 创建页面对象
login_page.navigate() # 2. 导航到登录页
login_page.login("user@test.com", "pass123") # 3. 执行业务动作
assert "/login" not in page.url # 4. 断言结果
关键点:
page参数无需手动创建 ---pytest-playwright插件自动注入浏览器实例- 测试函数只调
login_page.login(),不直接写page.fill()--- 这就是 POM 的核心 - 断言写在测试函数中,不写在页面对象中 --- 职责分离
2.4 企业级框架搭建
上面的骨架能跑,但在企业项目中还不够。需要补充的基础设施:
| 基础设施 | 解决什么问题 | 详见 |
|---|---|---|
| Config 配置管理 | 多环境 URL 切换、超时/浏览器参数化 | 第三篇 3.2 |
| TestDataManager | 测试数据与代码分离(JSON/CSV) | 第三篇 3.3 |
| Logger 日志 | 执行过程可追溯、失败可定位 | 第三篇 3.4 |
| conftest.py | Fixture 编排、失败截图、用例日志 | 第三篇 3.6 |
| 测试标记 | smoke/regression 分类、选择性执行 | 第三篇 3.8 |
| CLI 脚本 | 统一运行入口、参数化执行 | 第三篇 3.9 |
| 报告体系 | HTML/Allure 可视化报告 | 第三篇 3.11 |
2.5 运行测试
bash
# 最简运行
python -m pytest tests/ -v
# 指定浏览器 + 有头模式(调试用)
python -m pytest --browser chromium --headed
# 按标记过滤 + 生成报告
python -m pytest -m smoke --html=reports/report.html --self-contained-html
第三篇:落地项目实战
目标 Web 项目 UI 自动化测试框架:Playwright + Python + pytest
3.1 项目结构总览
ui_test/
├── config/ # 配置层:环境、浏览器、超时
│ └── config.py
├── pages/ # 页面对象层:每页一个类
│ ├── home_page.py
│ └── login_page.py
├── tests/ # 测试用例层:断言逻辑
│ └── test_login.py
├── utils/ # 工具层:BasePage、日志、数据、浏览器
│ ├── base_page.py
│ ├── browser_manager.py
│ ├── logger.py
│ └── test_data_manager.py
├── test_data/ # 测试数据(JSON/CSV)
├── screenshots/ # 失败截图输出
├── reports/ # 测试报告输出
├── conftest.py # pytest Fixture 与 Hook
├── pytest.ini # pytest 配置
├── run_tests.py # CLI 运行入口
└── .env # 环境变量(不入版本控制)
分层依赖方向:配置层 ← 工具层 ← 页面对象层 ← 测试用例层。上层只依赖直接下层,不跨层调用。
3.2 多环境配置体系
设计思路
用 .env 文件管理环境特定配置(URL、凭据),用 Config 类统一读取,支持 dev / test / prod 多环境切换。
.env 文件
ini
BASE_URL=https://test.your-web-app.com/en
TEST_ENV=test
BROWSER_HEADLESS=true
BROWSER_TIMEOUT=30000
# 敏感凭据(不入版本控制)
TEST_USERNAME=testuser@example.com
TEST_PASSWORD=your_password_here
Config 类核心设计
Config 类通过 os.getenv() 从 .env 读取配置,有默认值兜底。关键设计点:
- 环境变量优先:所有配置可通过环境变量覆盖,CI/CD 中直接注入即可
- 多环境字典 :
ENVIRONMENT_CONFIG维护 dev / test / prod 三套环境 - 自动目录创建 :
create_directories()在启动时创建 screenshots / reports / test\_data 目录 - 敏感信息隔离:凭据通过环境变量注入,不硬编码
bash
# 环境切换方式
# 方式 1:修改 .env 文件
# 方式 2:CI/CD 中注入环境变量
BROWSER_HEADLESS=true TEST_ENV=test python -m pytest tests/
3.3 测试数据管理
设计思路
测试数据与测试代码分离,支持 JSON(结构化凭据)和 CSV(参数化批量数据)两种格式。
数据文件示例
users.json(按用户类型组织):
json
{
"valid": { "email": "testuser@example.com", "password": "TestPassword123!" },
"invalid": { "email": "invalid@test.com", "password": "wrong_password" }
}
**test\_**cases.csv(参数化测试数据):
csv
test_id,email,password,expected_result
TC001,testuser@example.com,TestPassword123!,登录成功
TC002,invalid@test.com,wrong,登录失败
TestDataManager 核心能力
load_json_data()/load_csv_data()--- 加载两种格式数据get_user_data('valid')--- 按类型获取用户凭据get_random_data('email')--- 生成随机测试数据(邮箱/用户名/密码/手机号)
pytest 参数化测试
python
@pytest.mark.parametrize("email,password,expected", [
("testuser@example.com", "TestPassword123!", "成功"),
("invalid@test.com", "wrong", "失败"),
])
def test_login_scenarios(page, email, password, expected):
login_page = LoginPage(page)
login_page.navigate()
login_page.login(email, password)
if expected == "成功":
assert "/login" not in page.url
else:
assert login_page.is_error_displayed()
3.4 日志系统
设计思路
双通道输出:文件(DEBUG 级,完整记录)+ 控制台(INFO 级,关键信息)。日志文件按天命名,存放在 reports/logs/ 目录。
核心能力
logger.info()/logger.error()--- 标准日志级别logger.step("描述")--- 测试步骤标记,方便定位失败发生在哪一步logger.test_case_start()/logger.test_case_end()--- 用例边界标记
日志输出示例
2026-11-04 16:49:40 - test_login - INFO - 步骤: 步骤 1: 导航到首页
2026-11-04 16:49:42 - test_login - INFO - 步骤: 步骤 2: 点击 Sign In
2026-11-04 16:49:42 - test_login - INFO - 步骤: 步骤 3: 输入凭据
2026-11-04 16:49:42 - test_login - INFO - 步骤: 步骤 4: 执行登录
2026-11-04 16:49:42 - test_login - INFO - 步骤: 步骤 5: 验证结果
3.5 浏览器管理器
BrowserManager 封装 Playwright 浏览器完整生命周期(启动 → 上下文 → 页面 → 关闭),适用于脱离 pytest 独立运行脚本的场景。
注意:使用 pytest-playwright 时,
pagefixture 由插件自动管理,不需要 BrowserManager。它仅在编写独立运行脚本(如调试脚本、数据采集脚本)时使用。
生命周期:sync_playwright().start() → browser.launch() → browser.new_context() → context.new_page() → ... → 逐一 close()
3.6 conftest.py Fixture 编排
conftest.py 是 pytest 的全局配置文件,定义所有测试共享的 Fixture 和 Hook。
Fixture 体系
| Fixture | Scope | 作用 |
|---|---|---|
setup_test_environment |
session, autouse | 启动时创建目录、打印环境信息 |
test_data |
session | 初始化 TestDataManager |
test_case_logger |
function, autouse | 每个用例前后记录开始/结束日志 |
screenshot_on_failure |
function, autouse | 失败时自动全页截图 |
Hook 机制
pytest_runtest_makereport--- 捕获测试结果(通过/失败),挂到 item 上供 screenshot fixture 判断pytest_configure--- 注册自定义 marker(smoke / regression 等)pytest_collection_modifyitems--- 为所有测试自动添加ui标记
Fixture Scope 说明
| Scope | 生命周期 | 适用场景 |
|---|---|---|
session |
整个测试会话 | 环境初始化、数据管理器 |
function |
每个测试函数(默认) | 用例日志、截图 |
class |
每个测试类 | 类级共享数据 |
3.7 失败自动截图机制
失败截图是 UI 自动化的关键诊断手段,实现分三步:
- Hook 捕获结果 :
pytest_runtest_makereport将测试结果挂到item.rep_call - Fixture 判断并截图 :
screenshot_on_failure在 teardown 阶段检查rep_call.failed,若失败则调page.screenshot(full_page=True) - 命名规范 :
failure_{用例名}_{浏览器}_{时间戳}.png,便于关联到具体用例
3.8 测试标记与用例分类
pytest.ini 注册标记
ini
[tool:pytest]
markers =
smoke: 冒烟测试标记
regression: 回归测试标记
slow: 慢速测试标记
login: 登录相关测试标记
ui: UI界面测试标记
标记使用与执行
python
@pytest.mark.smoke
@pytest.mark.login
def test_sign_in_visible(self, page):
...
bash
python -m pytest -m smoke # 只跑冒烟
python -m pytest -m "login and regression" # 登录回归
python -m pytest -m "not slow" # 排除慢速
3.9 CLI 运行脚本
run_tests.py 封装 pytest 命令组装逻辑,提供统一入口:
bash
python run_tests.py --setup # 安装浏览器
python run_tests.py # 运行全部测试
python run_tests.py -m smoke -b firefox # 冒烟 + Firefox
python run_tests.py --headed -v # 有头模式调试
python run_tests.py -w 4 # 4 进程并行
python run_tests.py --allure # 生成 Allure 报告
核心逻辑:解析 argparse 参数 → 设置环境变量(浏览器/headless)→ 组装 pytest 命令 → subprocess 执行。
3.10 完整登录流程实战
被测网站分析
| 页面 | 元素 | 选择器 |
|---|---|---|
| 首页 | Sign In 链接 | a.btn-ghost[href='/en/login'] |
| 登录页 | 邮箱输入框 | input[name='email'] |
| 登录页 | 密码输入框 | input[name='password'] |
| 登录页 | 提交按钮 | button.auth-submit[type='submit'] |
测试流程
首页 → 点击 Sign In → 登录表单出现 → 填写凭据 → 点击提交 → 验证登录成功
断言策略:双重判断
登录成功的验证采用双重判断,兼容传统多页应用和 SPA 应用:
- URL 变化检查 (快速):登录成功后 URL 不再包含
/login - 表单消失检查(补充):如果 URL 未变化(SPA 可能不改变 URL),检查登录表单是否消失
python
# 先检查 URL(快速判断),再检查表单消失
current_url = page.url
url_changed = '/login' not in current_url
if not url_changed:
login_form_gone = not login_page.is_login_form_visible(timeout=3000)
else:
login_form_gone = True
assert login_form_gone or url_changed
3.11 测试报告体系
HTML 报告(pytest-html)
bash
# 注意:使用 3.2.0 版本,4.x 有 JS 渲染兼容问题(JS 动态渲染表格,内嵌预览面板不执行 JS 导致空白)
pip install pytest-html==3.2.0
python -m pytest --html=reports/report.html --self-contained-html
Allure 报告
bash
pip install allure-pytest
# 生成数据
python -m pytest --alluredir=reports/allure-results
# 生成可视化报告(需 allure CLI)
allure generate reports/allure-results -o reports/allure-report --clean
allure open reports/allure-report
Allure 提供趋势图、历史对比、用例分类树、步骤详情等可视化能力。
附录
A. 完整项目依赖清单
pytest==7.4.3
pytest-asyncio==0.21.1
pytest-html==3.2.0
pytest-xdist==3.3.1
pytest-playwright==0.4.3
playwright==1.40.0
allure-pytest==2.13.2
python-dotenv==1.0.0
B. 常用命令速查表
| 命令 | 说明 |
|---|---|
python -m playwright install |
安装浏览器二进制 |
python -m playwright codegen <url> |
录制脚本 |
python -m pytest tests/ -v |
运行所有测试 |
python -m pytest -m smoke |
运行冒烟测试 |
python -m pytest --browser firefox |
指定浏览器 |
python -m pytest --headed |
有头模式 |
python -m pytest -n 4 |
4 进程并行 |
python -m pytest --html=report.html --self-contained-html |
生成 HTML 报告 |
allure generate allure-results -o allure-report --clean |
生成 Allure 报告 |
C. Playwright 选择器速查
| 类型 | 语法 | 稳定性 |
|---|---|---|
| 测试 ID | page.get_by_test_id('login-btn') |
★★★★★ |
| 角色+名称 | page.get_by_role('button', name='Sign In') |
★★★★ |
| CSS 属性 | input[name='email'] |
★★★ |
| 文本 | text='Sign In' |
★★ |
| XPath | xpath=//button[@type="submit"] |
★ |