35个即跑即用的Playwright+Python UI测试脚本:从环境搭建到CI/CD集成
1. 项目概述:为什么你需要一套开箱即用的UI测试脚本?
如果你正在为Web应用的UI自动化测试发愁,脚本编写繁琐、环境配置复杂、报告不够直观,那么今天分享的这套“35个即跑即用的Playwright+Python UI测试脚本”可能就是你的解药。这不仅仅是一堆代码,而是一个经过实战打磨、覆盖了UI测试核心场景的解决方案包。它集成了登录、截图、Allure报告和PO模型等关键模块,旨在让你从“从零搭建框架”的泥潭中解脱出来,直接聚焦于业务测试逻辑本身。
在当前的敏捷开发和DevOps实践中,UI自动化测试是保障前端功能稳定性的重要防线。然而,很多团队卡在了第一步:如何快速搭建一个稳定、可维护且能产出漂亮报告的测试框架?Playwright以其强大的跨浏览器支持、快速的执行速度和可靠的自动等待机制,成为了新时代UI自动化的热门选择。配合Python的简洁语法,可以极大提升脚本开发效率。但光有工具不够,还需要好的“套路”和“模板”。这套脚本的价值,就在于它提供了从元素定位、页面封装、测试用例组织到报告生成的一整套最佳实践范例,你拿到手后,只需替换成自己项目的URL和元素选择器,就能立刻跑起来看到效果,极大降低了自动化测试的入门和集成门槛。
2. 环境准备与核心依赖安装
2.1 Python与Pip环境搭建
一切的基础是一个干净的Python环境。我强烈建议使用Python 3.8或更高版本,因为Playwright对新版本Python的支持和优化更好。如果你还没有安装Python,可以从官网下载安装包,记得在安装时勾选“Add Python to PATH”选项,这样就能在命令行中直接使用 python 和 pip 命令了。
安装完成后,打开终端(Windows上是CMD或PowerShell,Mac/Linux上是Terminal),输入以下命令验证安装是否成功:
python --version
pip --version
如果都能正确显示版本号,说明基础环境OK。接下来,我们需要一个项目目录。我习惯为每个测试项目创建独立的虚拟环境,这样可以避免不同项目间的依赖冲突。使用 venv 模块创建虚拟环境:
# 进入你的工作目录,例如 D:\Projects
cd D:\Projects
# 创建一个名为 playwright-auto-demo 的项目文件夹并进入
mkdir playwright-auto-demo && cd playwright-auto-demo
# 创建虚拟环境,环境文件夹名为 venv
python -m venv venv
创建完成后,激活虚拟环境:
- Windows (CMD):
venv\Scripts\activate.bat - Windows (PowerShell):
venv\Scripts\Activate.ps1(可能需要先执行Set-ExecutionPolicy RemoteSigned -Scope CurrentUser) - Mac/Linux:
source venv/bin/activate
激活后,命令行提示符前通常会显示 (venv) ,表示你已进入该虚拟环境。
2.2 Playwright与测试框架安装
在激活的虚拟环境中,我们开始安装核心依赖。首先通过pip安装Playwright的Python库:
pip install playwright
这个命令会安装Playwright的核心Python绑定。安装完成后,我们需要安装Playwright所需的浏览器二进制文件(Chromium, Firefox, WebKit)。Playwright很贴心地提供了一条命令来完成:
playwright install
这条命令会下载所有支持的浏览器,虽然会占用一些磁盘空间(约1.5GB),但保证了测试环境的完整性。如果你确定只测试Chrome(或基于Chromium的Edge),可以使用 playwright install chromium 来只安装Chromium,以节省时间和空间。
接下来,安装测试运行框架和报告生成工具。这里我们选择 pytest ,因为它功能强大、插件丰富,是Python生态中最主流的测试框架。同时安装用于生成美观HTML报告的 allure-pytest ,以及用于HTTP请求测试的 pytest-playwright (它提供了对Playwright的pytest fixtures支持):
pip install pytest allure-pytest pytest-playwright
为了确保依赖版本兼容,我通常会固定一组经过验证的版本。你可以创建一个 requirements.txt 文件来管理依赖:
playwright==1.40.0
pytest==7.4.4
allure-pytest==2.13.2
pytest-playwright==0.4.3
然后使用 pip install -r requirements.txt 一次性安装。
注意:安装Playwright浏览器时,可能会因为网络问题导致下载缓慢或失败。如果遇到这种情况,可以尝试设置环境变量
PLAYWRIGHT_DOWNLOAD_HOST为国内镜像源,例如https://npmmirror.com/mirrors/playwright/。具体方法是在执行playwright install前,在命令行中先执行set PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright/(Windows) 或export PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright/(Mac/Linux)。
3. 项目结构设计与PO模型解析
3.1 为什么采用PO模型?
拿到35个脚本,第一眼可能会觉得文件很多。别慌,其核心组织思想是“页面对象模型”。PO模型是一种设计模式,它将每个Web页面抽象成一个类,页面上的元素定位器和操作这些元素的方法都封装在这个类中。这样做的好处非常明显:
- 高可维护性 :当页面UI发生变化时,你只需要修改对应的页面对象类中的元素定位器,所有使用该定位器的测试用例都会自动生效,无需四处修改。
- 高可读性 :测试用例脚本中不再充斥着一堆复杂的CSS或XPath选择器,而是像“
login_page.enter_username('admin')”这样贴近自然语言的语句,业务逻辑一目了然。 - 低耦合性 :页面对象与测试用例分离,便于团队协作。前端开发改页面,测试人员只需更新对应的PO类。
3.2 标准项目目录结构
一个典型的基于PO模型和pytest的Playwright项目结构如下所示。这35个脚本就是按照这个逻辑组织的,你可以参考这个结构来理解它们:
playwright-auto-demo/
├── requirements.txt # 项目依赖列表
├── conftest.py # Pytest全局配置,如Playwright browser fixture定义
├── pytest.ini # Pytest配置文件,可设置命令行默认参数、标记等
├── pages/ # 页面对象模型(PO)目录
│ ├── __init__.py
│ ├── base_page.py # 所有页面对象的基类,封装公共方法(如打开URL、通用等待)
│ ├── login_page.py # 登录页面对象
│ ├── home_page.py # 主页页面对象
│ └── ... # 其他页面对象
├── tests/ # 测试用例目录
│ ├── __init__.py
│ ├── test_login.py # 登录相关测试用例
│ ├── test_screenshot.py # 截图相关测试用例
│ └── ... # 其他测试用例集
├── test_data/ # 测试数据目录(如JSON, YAML, CSV文件)
│ └── users.json
├── reports/ # 测试报告输出目录(Allure报告生成于此)
│ └── allure-results/ # Allure原始结果数据
├── screenshots/ # 自动截图保存目录
│ └── 20240101_120000_failed_login.png
└── utils/ # 工具函数目录
├── __init__.py
├── helpers.py # 通用帮助函数,如数据生成、文件读取
└── allure_helpers.py # 定制Allure报告的辅助函数
在这个结构中, pages 文件夹里的每个 .py 文件代表一个页面, tests 文件夹里的文件代表一组测试用例。 conftest.py 是pytest的“魔法”文件,里面可以定义一些全局的fixture,比如初始化Playwright浏览器,这样所有测试用例都可以直接使用,无需重复编写。
4. 核心脚本模块深度拆解
4.1 登录模块的健壮性实现
登录是大多数Web应用自动化测试的起点,也是最容易出错的环节。一个健壮的登录脚本需要考虑多种场景:正常登录、用户名错误、密码错误、空输入、验证码(如果有)等。在提供的脚本中,你会看到 test_login.py 里包含了这些场景的测试用例。
其核心依赖于 pages/login_page.py 这个页面对象。我们来看看一个典型的登录页面对象是如何封装的:
# pages/login_page.py
from playwright.sync_api import Page
from .base_page import BasePage
class LoginPage(BasePage):
# 元素定位器,使用字典或类属性管理,清晰易改
_username_input = "#username"
_password_input = "#password"
_login_button = "button[type='submit']"
_error_message = ".alert-error"
def __init__(self, page: Page):
super().__init__(page)
self.page = page
def navigate_to_login(self):
"""导航到登录页面"""
self.page.goto(f"{self.base_url}/login")
self.wait_for_element(self._username_input) # 使用基类的等待方法
def enter_credentials(self, username: str, password: str):
"""输入用户名和密码"""
self.page.fill(self._username_input, username)
self.page.fill(self._password_input, password)
def click_login(self):
"""点击登录按钮"""
self.page.click(self._login_button)
def get_error_message(self):
"""获取错误提示信息"""
return self.page.text_content(self._error_message) if self.page.is_visible(self._error_message) else None
def perform_login(self, username: str, password: str):
"""一站式登录操作:输入并提交"""
self.enter_credentials(username, password)
self.click_login()
在测试用例中,使用这个页面对象就非常简洁:
# tests/test_login.py
import pytest
from pages.login_page import LoginPage
def test_successful_login(page, login_page): # page和login_page是conftest.py中定义的fixture
login_page.navigate_to_login()
login_page.perform_login("valid_user", "valid_pass")
# 断言登录成功,例如跳转到首页或出现欢迎语
assert page.url == "https://example.com/dashboard"
assert page.is_visible("text=Welcome")
def test_login_with_invalid_password(login_page):
login_page.navigate_to_login()
login_page.perform_login("valid_user", "wrong_pass")
error_msg = login_page.get_error_message()
assert error_msg is not None
assert "密码错误" in error_msg
实操心得:对于登录这类关键操作,一定要加入明确的等待。脚本中
wait_for_element的使用至关重要,它确保页面元素加载完成后再进行操作,避免了因网络延迟或前端渲染导致的“Element not found”错误。此外,将登录操作封装成perform_login方法,不仅简化了测试用例,也便于未来如果登录流程增加步骤(如输入验证码)时,只需修改这一处。
4.2 智能截图与失败追踪
UI测试光看通过/失败不够直观,我们需要“看见”发生了什么。Playwright原生支持截图,但如何有效利用是关键。脚本中提供了多种截图策略:
- 失败自动截图 :通过pytest的钩子函数,在测试用例失败时自动截取当前页面状态,并保存到指定目录,文件名包含时间戳和用例名,便于追溯。
- 关键步骤截图 :在测试用例中主动对重要操作(如提交表单前、获取结果后)进行截图,作为测试证据。
- 全屏与元素截图 :不仅可以截取整个页面,还可以针对特定元素(如一个弹窗、一个表格)进行精准截图。
实现失败自动截图通常需要在 conftest.py 中编写一个pytest钩子:
# conftest.py
import pytest
from datetime import datetime
import os
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""
获取测试用例执行结果,并在失败时截图。
"""
outcome = yield
report = outcome.get_result()
if report.when == "call" and report.failed:
# 获取测试用例中的page fixture
page = item.funcargs.get("page")
if page:
# 创建截图目录
screenshot_dir = "screenshots"
os.makedirs(screenshot_dir, exist_ok=True)
# 生成带时间戳和用例名的文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
test_name = item.name
file_name = f"{screenshot_dir}/{timestamp}_{test_name}.png"
# 执行截图
page.screenshot(path=file_name, full_page=True) # full_page=True截取完整页面
# 可以将截图路径附加到Allure报告中(后续章节讲)
if hasattr(report, 'extra'):
from allure_commons.types import AttachmentType
import allure
allure.attach(page.screenshot(full_page=True), name=f"screenshot_{test_name}", attachment_type=AttachmentType.PNG)
在测试用例中,你也可以主动截图:
def test_checkout_process(page):
# ... 一些操作
page.screenshot(path="screenshots/before_payment.png")
# ... 点击支付
page.screenshot(path="screenshots/after_payment.png")
# 或者只截取某个元素
order_summary = page.locator(".order-summary")
order_summary.screenshot(path="screenshots/order_details.png")
注意事项:截图会占用磁盘空间,尤其是
full_page=True截取长图时。建议定期清理旧的截图文件,或者配置只在失败时截图。另外,截图文件名最好包含足够的信息(如用例ID、时间戳),方便在大量文件中快速定位。
4.3 生成专业级Allure测试报告
控制台输出的文字报告不够直观,Allure报告能以清晰的树状结构展示测试套件、用例,并附上步骤、截图、日志,生成非常专业的HTML报告。脚本集成了 allure-pytest ,让你一键生成漂亮报告。
首先,在测试用例和页面对象方法中,使用Allure的装饰器和步骤记录来增强报告可读性:
# tests/test_login.py
import allure
import pytest
@allure.epic("用户认证模块") # 定义特性(大模块)
@allure.feature("登录功能") # 定义功能点
class TestLogin:
@allure.story("正向用例:使用正确凭据登录") # 定义用户故事
@allure.title("验证管理员用户登录成功") # 定义测试用例标题
@allure.severity(allure.severity_level.CRITICAL) # 定义严重级别
def test_admin_login_success(self, login_page):
with allure.step("步骤1: 导航到登录页面"):
login_page.navigate_to_login()
with allure.step("步骤2: 输入管理员用户名和密码"):
login_page.enter_credentials("admin", "admin123")
with allure.step("步骤3: 点击登录按钮"):
login_page.click_login()
with allure.step("步骤4: 验证登录成功,跳转到控制台"):
# 断言...
assert login_page.page.url.contains("dashboard")
在 pages/login_page.py 中,也可以为关键操作添加步骤:
import allure
class LoginPage(BasePage):
@allure.step("在登录页面输入用户名 '{username}' 和密码")
def enter_credentials(self, username: str, password: str):
self.page.fill(self._username_input, username)
self.page.fill(self._password_input, password)
allure.attach(f"输入的用户名: {username}, 密码长度: {len(password)}", name="登录凭证", attachment_type=allure.attachment_type.TEXT)
运行测试并生成报告需要两步:
- 运行测试并收集结果 :使用pytest运行测试,并指定
--alluredir参数来存放原始的Allure结果数据。pytest tests/ --alluredir=./reports/allure-results -v-v参数表示详细输出。 - 生成HTML报告 :使用Allure命令行工具将上一步收集的结果数据转换成HTML报告。
然后打开生成的HTML报告:allure generate ./reports/allure-results -o ./reports/allure-report --clean
报告会在你的默认浏览器中打开,你可以看到清晰的测试套件层级、通过率、每个用例的详细步骤、截图和附件。allure open ./reports/allure-report
实操心得:为了将失败截图自动附加到Allure报告中,我们需要增强之前
conftest.py中的钩子函数。在用例失败时,不仅保存截图到本地,也使用allure.attach方法将截图以附件形式添加到报告中。这样在查看Allure报告时,可以直接在失败用例的详情里看到截图,无需再去本地文件夹翻找,极大提升了问题排查效率。
4.4 页面对象模型的进阶封装技巧
基础的PO模型是将页面元素和方法封装在一起。在复杂的项目中,我们可以进一步优化:
- 基类封装通用操作 :创建一个
BasePage类,所有具体的页面对象都继承它。BasePage中封装诸如wait_for_element,click_element,get_text等几乎所有页面都会用到的方法,以及base_url等公共属性。# pages/base_page.py from playwright.sync_api import Page, expect import allure class BasePage: def __init__(self, page: Page): self.page = page self.base_url = "https://your-app.com" # 可从配置读取 def navigate(self, url_suffix=""): full_url = f"{self.base_url}{url_suffix}" with allure.step(f"导航到: {full_url}"): self.page.goto(full_url) def wait_for_element(self, selector, state="visible", timeout=30000): """等待元素达到特定状态""" locator = self.page.locator(selector) if state == "visible": locator.wait_for(state="visible", timeout=timeout) elif state == "hidden": locator.wait_for(state="hidden", timeout=timeout) return locator def take_screenshot(self, name): """截图并附加到Allure报告""" allure.attach( self.page.screenshot(full_page=True), name=name, attachment_type=allure.attachment_type.PNG ) - 组件化封装 :对于网站中重复出现的组件,如导航栏、页脚、模态框,可以单独封装成组件类。页面对象再组合这些组件。
# pages/components/navbar.py class NavBar: def __init__(self, page: Page): self.page = page self._user_menu = "#user-menu" def logout(self): self.page.click(self._user_menu) self.page.click("text=退出登录") # pages/dashboard_page.py from .components.navbar import NavBar class DashboardPage(BasePage): def __init__(self, page: Page): super().__init__(page) self.navbar = NavBar(page) # 组合导航栏组件 - 使用数据驱动测试 :将测试数据(如不同的用户名密码组合)从测试脚本中分离出来,存放在JSON、YAML或CSV文件中。使用pytest的
@pytest.mark.parametrize装饰器来实现数据驱动,使一个测试用例可以覆盖多组数据。# test_data/login_data.json [ {"username": "admin", "password": "correct", "expected": "success"}, {"username": "admin", "password": "wrong", "expected": "fail"}, {"username": "", "password": "correct", "expected": "fail"} ] # tests/test_login_ddt.py import json import pytest with open('./test_data/login_data.json', 'r') as f: test_data = json.load(f) @pytest.mark.parametrize("data", test_data) def test_login_with_data(login_page, data): login_page.perform_login(data['username'], data['password']) if data['expected'] == 'success': assert login_page.is_login_successful() else: assert login_page.get_error_message() is not None
5. 脚本的即跑即用与定制化
5.1 如何运行现有脚本?
假设你已经下载或克隆了这35个脚本,并按照第二章配置好了环境。运行它们通常只需几步:
- 检查配置 :打开项目根目录下的
conftest.py或配置文件,查看base_url是否指向一个可访问的测试环境。如果是像https://example.com这样的公共演示网站,可以直接运行。如果是内部系统,你需要将其修改为你自己测试环境的地址。 - 运行全部测试 :在项目根目录下打开终端,激活虚拟环境后,运行最基本的pytest命令。
这条命令会自动发现并运行pytesttests目录下所有以test_开头的文件。 - 运行特定模块或用例 :
pytest tests/test_login.py # 运行单个文件 pytest tests/test_login.py::TestLogin # 运行单个测试类 pytest tests/test_login.py::TestLogin::test_admin_login_success # 运行单个测试方法 pytest -k "login" # 运行名称中包含“login”的所有用例 pytest -m "smoke" # 运行标记为smoke的用例(需要在用例上用@pytest.mark.smoke装饰) - 生成并查看报告 :
# 运行测试并收集Allure结果 pytest --alluredir=./reports/allure-results # 生成HTML报告 allure generate ./reports/allure-results -o ./reports/allure-report --clean # 打开报告 allure open ./reports/allure-report
5.2 将脚本适配到你自己的项目
“即跑即用”的核心是“模板”。你需要将这些脚本作为模板,修改关键部分以适配你的实际项目:
- 修改页面元素定位器 :这是最主要的工作。打开
pages/目录下的各个页面对象文件,将里面的CSS选择器或XPath(如#username,button[type='submit'])替换成你目标网页上对应元素的真实定位器。使用浏览器的开发者工具(F12)来检查元素并复制选择器。Playwright Recorder(下文会讲)也能极大帮助这件事。 - 更新基础URL和导航逻辑 :在
base_page.py或项目配置中,将base_url改为你自己应用的URL。检查各个页面对象的navigate_to_xxx方法中的路径是否正确。 - 调整测试数据和断言 :在测试用例文件(
tests/目录下)中,将测试用的用户名、密码、商品ID等数据替换成你测试环境有效的账号。同时,将断言(assert语句)中的预期结果(如跳转后的URL、页面出现的文本)修改为你的应用实际的行为。 - 处理特殊验证 :如果你的登录有验证码、短信验证等环节,脚本中可能没有直接处理。你需要根据情况:
- 测试环境关闭验证码 :这是最常用的方式,让开发在测试环境提供万能验证码或关闭验证码功能。
- OCR识别 :对于简单图形验证码,可以考虑集成OCR库(如
pytesseract)进行识别,但稳定性和复杂度较高。 - 接口绕过 :与开发协商,通过调用后端接口获取或绕过验证码(这需要一定的接口测试知识)。
注意:自动化测试的原则是测试核心业务流程,像验证码这种专门设计来防止机器人的环节,通常建议在测试环境将其屏蔽或简化处理。
6. 高效编写与维护脚本的实战技巧
6.1 利用Playwright CodeGen快速生成脚本骨架
对于全新的页面,手动编写所有元素操作很耗时。Playwright提供了一个强大的命令行代码生成器,可以录制你的操作并生成Python(或其他语言)脚本。
playwright codegen https://your-test-site.com
执行上述命令会自动打开一个浏览器和一个代码生成窗口。你在浏览器中的所有操作(点击、输入、导航)都会实时转换成代码显示在窗口中。录制完成后,你可以将生成的代码复制到你的页面对象或测试用例中,再进行结构化和封装。这是快速创建初始脚本原型的利器。
6.2 元素定位策略与最佳实践
稳定的元素定位是UI自动化的基石。Playwright支持多种定位方式,优先级建议如下:
- Role定位(推荐) :通过元素的ARIA角色、名称进行定位,这是最接近用户感知的方式,可读性最好,且通常不易因前端微调而失效。
page.get_by_role("button", name="登录").click() page.get_by_role("textbox", name="用户名").fill("admin") - Text定位 :通过页面上的文本内容定位。
page.get_by_text("提交订单").click() page.get_by_text("欢迎", exact=True).is_visible() # exact=True 表示精确匹配 - CSS选择器 :最常用和灵活的方式。优先使用
id、有意义的class或属性。page.locator("#submit-button").click() page.locator(".primary-btn").click() page.locator("input[name='email']").fill("test@example.com") - XPath(谨慎使用) :功能强大但脆弱,容易因页面结构微小变动而失效。除非没有其他选择,否则尽量避免使用绝对路径或包含索引的XPath。
# 尽量避免 page.locator("//div[@id='container']/div[3]/button[2]").click() # 相对好一些 page.locator("//button[contains(text(), '保存')]").click()
实操心得:为重要的元素(如核心按钮、表单输入框)在开发阶段就争取加上稳定的
id或data-testid属性,这是对自动化测试最友好的做法。例如<button data-testid="login-submit-btn">登录</button>,这样你可以用page.locator('[data-testid="login-submit-btn"]')进行非常稳定的定位。
6.3 等待机制:告别“sleep”和“flaky tests”
不稳定的测试(有时成功有时失败)往往是等待处理不当造成的。Playwright内置了自动等待机制,对于大多数操作(如 click , fill )它会等待元素可操作。但有些情况需要显式处理:
- 等待导航 :
page.goto(url, wait_until='networkidle')会等待到网络基本空闲。 - 等待元素出现/消失 :使用
page.locator(selector).wait_for(state='visible')。 - 等待特定条件 :使用
page.wait_for_function()或page.wait_for_url()。 - 等待超时设置 :可以在全局或局部设置更长的超时时间
page.set_default_timeout(60000)。
绝对避免使用硬编码的 time.sleep(seconds) ,这是测试脚本性能低下和不稳定的罪魁祸首。
6.4 测试用例的组织与标记
随着脚本增多,需要良好的组织。Pytest提供了丰富的标记功能来对用例进行分类:
import pytest
@pytest.mark.smoke # 冒烟测试
def test_quick_login():
pass
@pytest.mark.regression # 回归测试
@pytest.mark.slow # 慢速测试
def test_complete_user_journey():
pass
@pytest.mark.parametrize("browser_name", ["chromium", "firefox"]) # 参数化,跨浏览器测试
def test_cross_browser(browser_name, page): # page fixture会根据browser_name自动创建对应浏览器上下文
pass
然后可以通过标记来选择性运行:
pytest -m "smoke" # 只运行冒烟测试
pytest -m "not slow" # 不运行标记为slow的测试
7. 常见问题排查与性能优化
7.1 典型问题速查表
在实际运行脚本时,你可能会遇到以下常见问题:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
Error: Page closed |
测试过程中页面被意外关闭或导航。 | 检查脚本逻辑,确保在操作元素前页面处于稳定状态。使用 page.wait_for_load_state() 确保页面加载完成。 |
Timeout 30000ms exceeded |
元素未在默认30秒内出现或变为可操作状态。 | 1. 检查元素定位器是否正确,页面是否已加载。 2. 增加超时时间: page.locator(selector).click(timeout=60000) 。 3. 检查是否有模态框、弹窗遮挡了目标元素。 |
Element is not visible |
元素存在于DOM中,但被隐藏(CSS: display: none )或不可交互。 |
使用 page.locator(selector).is_visible() 判断。可能需要触发某些操作(如点击下拉箭头)才能使元素显示。 |
Target closed |
浏览器上下文或页面在操作前已被关闭。 | 检查fixture的生命周期。确保在 @pytest.fixture(scope="function") 的页面对象中,不要在一个用例中关闭了被其他fixture共享的浏览器。 |
| Allure报告为空或只有框架 | 1. 未正确使用 @allure.step 等装饰器。 2. 运行命令未指定 --alluredir 。 3. allure-results 目录被清空后才生成报告。 |
1. 确保用例或页面方法中添加了Allure注解。 2. 运行pytest时务必加上 --alluredir=路径 。 3. 先 generate 再 open , --clean 参数会清空旧结果。 |
| 截图是空白或纯色 | 1. 截图时机不对,页面还未渲染。 2. 在 headless (无头)模式下,某些WebGL或复杂动画可能导致问题。 |
1. 截图前加入等待: page.wait_for_timeout(1000) 或等待特定元素。 2. 尝试以非无头模式运行:在 conftest.py 的browser fixture中设置 headless=False 。 |
| 脚本在CI/CD中失败,本地却成功 | 1. CI环境缺少依赖(如浏览器未安装)。 2. CI环境资源(CPU/内存)不足。 3. 网络延迟或测试环境在CI中不可达。 |
1. 在CI脚本中确保运行了 playwright install 。 2. 为Playwright配置更长的超时和更少的并行worker。 3. 检查CI环境到测试环境的网络连通性。 |
7.2 性能优化与稳定运行
- 复用浏览器上下文 :在
conftest.py中,通过设置browser fixture的scope为"session",可以让所有测试用例共享同一个浏览器实例,而不是每个用例都启动关闭一次,这能极大提升测试速度。
注意,共享浏览器上下文时,每个测试用例应该使用独立的@pytest.fixture(scope="session") def browser(): with sync_playwright() as p: # 可以在这里设置慢速网络、模拟移动设备等 browser = p.chromium.launch(headless=True) # CI环境通常用headless yield browser browser.close()page(scope="function")来保证测试隔离。 - 并行测试 :Pytest支持通过
pytest-xdist插件进行并行测试。安装后使用-n参数指定进程数。
并行时要注意测试用例之间的独立性,不能有共享状态(如操作同一个全局变量、依赖固定的执行顺序)。pip install pytest-xdist pytest -n auto # 自动检测CPU核心数并行运行 - 选择性运行与失败重试 :
- 使用
pytest -k关键字过滤运行相关用例。 - 使用
pytest --lf(--last-failed)只重新运行上次失败的用例。 - 安装
pytest-rerunfailures插件,对不稳定的测试进行自动重试。pip install pytest-rerunfailures pytest --reruns 3 --reruns-delay 2 # 失败后重试3次,每次间隔2秒
- 使用
- 资源清理 :确保测试用例在结束时清理测试数据(如删除测试创建的用户、订单),避免测试数据污染影响后续用例。这通常可以通过
@pytest.fixture的teardown操作,或者调用后台清理接口来实现。
7.3 集成到CI/CD流水线
自动化测试只有集成到CI/CD中才能发挥最大价值。以GitHub Actions为例,一个简单的配置可能如下:
# .github/workflows/playwright-test.yml
name: Playwright UI Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
pip install -r requirements.txt
playwright install --with-deps chromium # 只安装Chromium以加快速度
- name: Run tests
run: |
pytest --alluredir=allure-results
env:
BASE_URL: ${{ secrets.TEST_ENV_URL }} # 从GitHub Secrets读取测试环境URL
- name: Upload Allure results
uses: actions/upload-artifact@v3
with:
name: allure-results
path: allure-results/
# 可以添加后续步骤:生成并部署Allure报告到静态页面服务
这个工作流会在每次代码推送或PR时自动运行UI测试,并将原始的Allure结果保存为制品,供后续分析或生成报告。
将35个脚本作为种子,结合上述的设计思想、实操技巧和避坑指南,你完全可以根据自己项目的实际情况,生长出一套健壮、高效且易于维护的UI自动化测试体系。记住,自动化测试不是一蹴而就的,而是需要随着项目迭代不断维护和优化的活文档。从核心业务流程开始,逐步覆盖,持续集成,才能真正为你的产品质量保驾护航。
更多推荐


所有评论(0)