Python+pytest+Playwright:从Web自动化测试到RPA的工程化实践
1. 项目概述:为什么选择这个技术栈?
如果你正在寻找一个既能做Web自动化测试,又能无缝衔接RPA(机器人流程自动化)任务的技术方案,那么“Python + pytest + Playwright”这个组合,绝对值得你投入时间深入研究。这不仅仅是又一个测试框架的教程,而是一套面向现代Web应用和业务流程自动化的完整工程化解决方案。
我最初接触这个组合,是为了解决一个棘手的业务问题:公司内部有一个每日需要人工登录多个后台系统、导出数据、合并报表的重复性流程。传统的UI自动化测试工具往往只关注“测试验证”,在稳定执行复杂业务流程、处理动态元素、管理多页面状态等方面显得力不从心。而Playwright以其强大的浏览器上下文控制、自动等待和网络拦截能力脱颖而出,pytest则提供了极其灵活和强大的测试组织、夹具管理和报告生成能力。用Python将它们粘合起来,你就得到了一把既能做精准测试,又能扛起RPA重任的瑞士军刀。
简单来说,这个指南的核心价值在于:将Playwright的自动化执行能力,通过pytest的工程化框架进行管理和驱动,最终用Python脚本实现稳定、可维护、可扩展的Web自动化任务。无论是前端开发需要做E2E测试,还是业务人员想自动化日常的网页操作,这套方案都能提供一个高起点的最佳实践。接下来,我会带你从零开始,拆解每一个环节,分享我趟过的坑和总结的技巧。
2. 环境搭建与核心工具选型解析
工欲善其事,必先利其器。搭建一个稳定且高效的开发环境是第一步,这里的选择会直接影响后续的开发体验和脚本稳定性。
2.1 Python环境与包管理策略
我强烈建议使用conda或venv创建独立的虚拟环境,这能完美隔离项目依赖,避免不同项目间包版本的冲突。对于新手,Miniconda是个不错的选择,它比完整的Anaconda更轻量。
# 创建并激活一个名为 `web-auto` 的虚拟环境 conda create -n web-auto python=3.9 conda activate web-auto为什么选择Python 3.9?这是一个在稳定性和新特性之间取得很好平衡的版本,绝大多数库的兼容性都很好。版本太高(如3.11+)有时会遇到一些底层依赖的编译问题,版本太低(如3.6)则可能无法使用Playwright的一些新API。
包管理方面,使用pip安装核心依赖。这里有个关键点:固定核心库的版本。自动化脚本的稳定性至关重要,依赖库的意外升级可能导致API变更或行为变化,从而引发批量失败。
# 在项目根目录的 requirements.txt 中明确版本 playwright==1.40.0 pytest==7.4.4 pytest-playwright==0.4.3 pytest-html==4.1.1 pytest-xdist==3.5.0pytest-playwright是官方维护的插件,它为我们提供了与pytest无缝集成的夹具(fixtures),比如page对象,让我们无需手动管理浏览器的启动和关闭,极大地简化了代码。
2.2 Playwright浏览器安装与配置要点
安装Playwright库后,需要安装它所需的浏览器内核。
# 安装Playwright Python库 pip install playwright # 安装Chromium, Firefox和WebKit浏览器内核 playwright install执行playwright install会下载Chromium、Firefox和WebKit的特定版本。这些不是你在系统里安装的Chrome或Edge,而是Playwright专门优化和控制的版本,确保了跨平台和跨版本的一致性,这是其稳定性的基石之一。
注意事项一:网络问题与镜像源如果下载速度慢或失败,可以设置环境变量使用国内镜像加速下载。例如,在终端中临时设置:
set PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright playwright install chromium或者将上述命令中的chromium替换为firefox、webkit或all。
注意事项二:只安装所需浏览器如果你的自动化场景只针对Chrome(或基于Chromium的Edge),为了节省磁盘空间和初始化时间,可以只安装Chromium:playwright install chromium。在CI/CD环境中,这一点尤其重要。
2.3 IDE选择与关键插件配置
Visual Studio Code (VSCode) 是目前进行Python自动化开发的首选,其丰富的插件生态能极大提升效率。
- Python插件 (Microsoft):提供智能补全、代码导航、调试、linting等核心功能。
- Pytest插件:可以让你直接在代码中点击运行测试,并在侧边栏看到清晰的测试树和结果。
- Playwright Test for VSCode:官方插件,提供录制、代码生成、测试列表查看和跟踪查看器(Trace Viewer)集成,非常强大。
配置好VSCode的Python解释器路径,指向你创建的虚拟环境web-auto。这样,你在VSCode终端里运行的命令就会自动在该环境下执行。
3. 项目结构设计与pytest核心概念
一个清晰的项目结构是维护性的生命线。对于自动化项目,我们不能把所有脚本都扔在一个文件夹里。
3.1 标准化项目目录布局
我推荐如下结构,它分离了测试逻辑、页面对象、数据和配置,符合经典的Page Object Model (POM) 模式,也方便未来扩展为RPA流程库。
web_auto_project/ ├── conftest.py # pytest全局配置文件,定义共享夹具 ├── pytest.ini # pytest主配置文件 ├── requirements.txt # 项目依赖 ├── tests/ # 测试用例目录 │ ├── __init__.py │ ├── test_login.py # 具体的测试用例文件 │ └── test_dashboard.py ├── pages/ # 页面对象模型目录 │ ├── __init__.py │ ├── login_page.py │ └── dashboard_page.py ├── utils/ # 工具函数目录 │ ├── __init__.py │ ├── data_helper.py │ └── api_client.py ├── data/ # 测试数据文件(如JSON, CSV) │ └── users.json ├── reports/ # 测试报告输出目录(由pytest-html生成) └── logs/ # 运行日志目录为什么采用POM?POM将页面的元素定位和操作封装成类,测试用例只关心业务逻辑。当页面UI发生变化时,你只需要修改对应的Page类,而不需要到处修改测试用例,这极大地提升了代码的可维护性和复用性。这对于RPA场景同样重要,一个“数据录入页面”的对象可以被多个不同的自动化流程调用。
3.2 pytest夹具(Fixture)的魔力
pytest的夹具系统是它的灵魂。你可以把夹具理解为测试的“前置条件”和“后置清理”的提供者。pytest-playwright插件已经为我们准备好了最关键的夹具。
在conftest.py中,我们可以定义和定制这些夹具:
import pytest from playwright.sync_api import Page, BrowserContext @pytest.fixture(scope="session") def browser_context_args(browser_context_args): """全局浏览器上下文配置,作用于所有测试""" # 例如:设置视窗大小、忽略HTTPS错误、设置地理位置等 return { **browser_context_args, "viewport": {"width": 1920, "height": 1080}, "ignore_https_errors": True, # "locale": "zh-CN", # 设置浏览器语言 } @pytest.fixture(scope="function") def login_page(page: Page): """一个自定义夹具:返回已初始化的登录页面对象,并确保每个测试函数开始时处于登出状态""" # 假设我们有一个清理登录状态的逻辑 # ... 例如清除cookie或访问登出URL from pages.login_page import LoginPage return LoginPage(page)关键参数解析:
scope:夹具的作用域。“session”(整个测试会话一次),“function”(每个测试函数一次,默认),“class”,“module”。合理设置作用域可以平衡执行速度和测试隔离性。浏览器启动 (browser) 通常用session,页面对象 (page) 用function。browser_context_args: 这个夹具允许我们定制浏览器上下文(Context)的启动参数。一个浏览器实例可以创建多个相互隔离的上下文,每个上下文包含独立的Cookie、LocalStorage等,非常适合模拟多用户并行操作,在RPA中处理多账户任务时非常有用。
3.3 pytest.ini配置文件详解
pytest.ini文件用于配置pytest的默认行为,让命令行更简洁。
[pytest] # 指定测试文件的位置和命名模式 testpaths = tests python_files = test_*.py python_classes = Test* python_functions = test_* # 添加命令行默认选项 addopts = -v # 详细输出 --tb=short # 发生错误时,打印简短的追溯信息 --strict-markers # 严格检查标记,避免拼写错误 -n auto # 使用pytest-xdist并行运行(如果安装了) --html=reports/report.html # 生成HTML报告 --self-contained-html # 生成独立的HTML报告(内联CSS/JS) # 自定义标记(markers),用于分类测试 markers = smoke: 冒烟测试 regression: 回归测试 slow: 运行缓慢的测试 rpa: 属于RPA流程的自动化任务通过这个配置,你只需要在终端运行pytest,它就会自动按照你的预设执行测试并生成报告。-n auto会调用所有CPU核心并行运行测试,能大幅缩短测试套件的总执行时间,对于大型自动化任务集效果显著。
4. Playwright核心API与最佳实践
Playwright的API设计非常直观,但要想写出稳定高效的脚本,必须理解其核心设计哲学:自动等待和严格模式。
4.1 元素定位与操作:告别“sleep”时代
Playwright最大的优点之一是其内置的智能等待。几乎所有操作(如click,fill,wait_for_selector)都会自动等待元素可操作(可见、启用、稳定等)。
# 传统方式(不稳定) page.click(“button#submit”) # 可能元素还没加载出来就点击,导致失败 # Playwright方式(稳定) page.click(“button#submit”) # 内部会自动等待该按钮可点击定位器(Locator)是核心概念:page.locator(selector)返回一个定位器对象,它代表一个或一组元素。所有操作都在定位器上进行。
# 创建定位器 submit_btn = page.locator(“button#submit”) # 操作前自动等待 submit_btn.click() # 断言前自动等待 expect(submit_btn).to_be_visible()最佳实践一:使用有弹性的选择器优先使用get_by_role,get_by_text,get_by_label等语义化定位方式,其次是get_by_test_id(需要开发配合添加># 好:通过角色和文本定位 page.get_by_role(“button”, name=“登录”).click() # 好:通过测试ID定位(最稳定) page.get_by_test_id(“login-submit”).click() # 一般:CSS选择器 page.locator(“.btn-primary”).click() # 尽量避免:脆弱的XPath page.locator(“//div[@id=‘container’]/button[2]”).click()
最佳实践二:善用wait_for_*方法处理动态内容对于异步加载的内容,除了操作自带的等待,可以显式使用:
# 等待导航完成 page.wait_for_url(“**/dashboard”) # 等待元素出现 page.wait_for_selector(“table.data-loaded”) # 等待某个条件成立 page.wait_for_function(“document.title.includes(‘完成’)")4.2 浏览器上下文与多页面管理
在RPA场景中,经常需要操作多个标签页,或者模拟多个独立的用户会话。BrowserContext就在这里派上用场。
def test_multi_session(browser): # 创建两个独立的上下文(如同两个完全隔离的浏览器) context1 = browser.new_context() context2 = browser.new_context() page1 = context1.new_page() page2 = context2.new_page() # page1和page2的cookies、localStorage完全隔离 page1.goto(“https://example.com/login”) page1.fill(“#username”, “user1”) # user2在page2上的登录状态不影响page1 # 任务完成后,记得关闭上下文 context1.close() context2.close()注意事项:资源管理务必在测试结束时关闭page和context。虽然pytest-playwright的夹具在函数作用域结束时通常会帮你清理,但在手动创建时,一定要成对地管理它们的生命周期,避免内存泄漏。
4.3 网络拦截与模拟(Mocking)
这是Playwright的杀手级功能,能让你完全控制页面的网络请求,非常适合:
- 屏蔽不必要的资源(如图片、样式表)以加速测试。
- 模拟后端API响应,实现前后端解耦的测试。
- 断言特定的网络请求是否发生。
# 拦截并修改请求 def test_intercept_api(page: Page): def handle_route(route): # 如果是特定的API请求,则返回模拟数据 if “/api/user/profile” in route.request.url: route.fulfill( status=200, content_type=“application/json”, body=json.dumps({“name”: “Mock User”, “level”: “VIP”}) ) else: # 其他请求继续正常进行 route.continue_() # 开始监听路由 page.route(“**/api/**”, handle_route) page.goto(“https://app.example.com”) # 此时页面收到的用户资料将是模拟数据这个功能在RPA中同样有用,比如你可以拦截某个导出请求,直接返回预设的Excel文件数据,从而跳过耗时的真实导出过程,快速验证后续的数据处理逻辑。
5. 编写可维护的测试用例与RPA流程脚本
有了稳固的基础设施,现在我们来编写真正的自动化脚本。我们的目标是将测试用例或RPA流程写得像“说明书”一样清晰。
5.1 使用Page Object Model封装页面
以登录页面为例,pages/login_page.py:
from playwright.sync_api import Page from typing import Tuple class LoginPage: def __init__(self, page: Page): self.page = page # 定位器定义在初始化方法中,便于集中管理 self.username_input = page.get_by_label(“用户名或邮箱”) self.password_input = page.get_by_label(“密码”) self.submit_button = page.get_by_role(“button”, name=“登录”) self.error_message = page.locator(“.alert-error”) def navigate(self): """导航到登录页""" self.page.goto(“/login”) return self def fill_credentials(self, username: str, password: str) -> “LoginPage”: """填写用户名和密码""" self.username_input.fill(username) self.password_input.fill(password) return self # 支持链式调用 def submit(self) -> “LoginPage”: """点击登录按钮""" self.submit_button.click() return self def get_error_text(self) -> str: """获取错误提示文本""" return self.error_message.inner_text() def perform_login(self, username: str, password: str): """完整的登录流程(组合操作)""" (self.navigate() .fill_credentials(username, password) .submit()) # 等待登录成功,例如跳转到首页或出现用户菜单 self.page.wait_for_url(“**/dashboard”)设计要点:
- 方法返回
self:支持链式调用,让代码更流畅,如login_page.navigate().fill_credentials(...).submit()。 - 操作与断言分离:页面对象只负责操作和获取状态,断言放在测试用例中。
- 提供高层业务方法:如
perform_login,将多个低阶操作封装成一个有业务意义的步骤,供测试用例或RPA主流程调用。
5.2 组织清晰的测试用例
在tests/test_login.py中:
import pytest from pages.login_page import LoginPage class TestLogin: """登录功能测试集""" @pytest.mark.smoke @pytest.mark.rpa # 标记为RPA核心流程 def test_successful_login(self, login_page: LoginPage): """测试正常登录流程""" # 使用页面对象的高层方法 login_page.perform_login(“valid_user”, “valid_pass”) # 断言:使用Playwright的断言库,它也会自动等待 from playwright.sync_api import expect expect(login_page.page).to_have_url(“**/dashboard”) expect(login_page.page.locator(“#user-menu”)).to_be_visible() @pytest.mark.parametrize(“username, password, expected_error”, [ (“”, “somepass”, “用户名不能为空”), (“user”, “”, “密码不能为空”), (“wrong”, “wrong”, “用户名或密码错误”), ]) def test_login_failure(self, login_page: LoginPage, username, password, expected_error): """参数化测试:多种错误登录场景""" (login_page.navigate() .fill_credentials(username, password) .submit()) # 断言错误信息正确 actual_error = login_page.get_error_text() assert expected_error in actual_error, f“期望错误包含‘{expected_error}’,实际得到‘{actual_error}’” def test_login_as_part_of_rpa_flow(self, page: Page): """模拟一个RPA流程:登录后执行一系列操作""" login_page = LoginPage(page) login_page.perform_login(“rpa_bot”, “secure_password”) # 接下来是RPA流程的其他步骤... # 1. 导航到订单页面 page.goto(“/orders”) # 2. 导出今日订单数据 page.locator(“button:has-text(‘导出CSV’)”).click() # 3. 等待下载完成并验证文件 # ... 这里会用到Playwright的下载事件监听 # 最终的业务断言 expect(page.locator(“.export-success-message”)).to_be_visible()技巧:
- 使用
@pytest.mark.parametrize进行数据驱动测试,避免为相似场景编写重复代码。 - 为测试用例打上自定义标记(如
@pytest.mark.rpa),方便通过pytest -m rpa只运行RPA相关的流程。 - 断言时优先使用
playwright.sync_api.expect,它比普通的assert更强大,内置了等待和丰富的匹配器。
5.3 数据驱动与外部数据源
对于RPA,操作数据往往来自外部文件或数据库。我们可以将测试数据分离。
data/users.json:
[ {“role”: “admin”, “username”: “admin1”, “password”: “****”, “expected_home”: “/admin”}, {“role”: “user”, “username”: “user1”, “password”: “****”, “expected_home”: “/dashboard”} ]在测试中读取:
import json import pytest def load_test_data(file_path): with open(file_path, ‘r’, encoding=‘utf-8’) as f: return json.load(f) @pytest.mark.parametrize(“user_data”, load_test_data(“data/users.json”)) def test_login_with_different_roles(login_page: LoginPage, user_data): login_page.perform_login(user_data[“username”], user_data[“password”]) expect(login_page.page).to_have_url(f“**{user_data[‘expected_home’]}”)6. 高级技巧与RPA场景深度集成
当基础自动化跑通后,我们会面临更复杂的场景:文件操作、定时任务、异常恢复、与其它系统集成等。
6.1 处理文件上传与下载
Playwright让文件交互变得简单。
# 文件上传(无需触发系统文件选择框) page.locator(“input[type=‘file’]”).set_input_files([“path/to/file1.pdf”, “path/to/file2.jpg”]) # 监听文件下载 with page.expect_download() as download_info: page.locator(“button#export-csv”).click() download = download_info.value # 建议将文件保存到特定目录,并可以读取其内容进行校验 save_path = f“./downloads/{download.suggested_filename}” download.save_as(save_path) # 读取下载的CSV文件内容进行验证 import pandas as pd df = pd.read_csv(save_path) assert not df.empty, “下载的文件内容为空”6.2 使用pytest-xdist实现并行化
对于拥有大量独立自动化任务的RPA流程,并行执行是提升效率的关键。pytest-xdist插件与pytest-playwright兼容良好。
# 启动与CPU核心数相同的worker进程并行运行测试 pytest -n auto # 指定使用3个worker pytest -n 3并行化注意事项:
- 会话级夹具:确保
browser夹具的scope=“session”,这样所有worker共享同一个浏览器实例(但会有独立的上下文),效率最高。 - 资源隔离:确保每个测试用例(或RPA任务)是独立的,不依赖共享的全局状态(如固定的用户账号)。可以通过动态生成测试数据或使用不同的上下文来实现隔离。
- 日志与报告:并行运行时,控制台输出会交错。使用
pytest-html等插件生成合并的报告,或者将每个worker的日志输出到单独的文件。
6.3 异常处理与流程健壮性
RPA流程需要面对不稳定的生产环境。健壮的脚本必须包含异常处理和恢复逻辑。
def robust_rpa_flow(page: Page, max_retries: int = 3): """一个包含重试机制的RPA流程函数""" for attempt in range(1, max_retries + 1): try: # 步骤1: 登录 login_page = LoginPage(page) login_page.perform_login(“bot_user”, “pwd”) # 步骤2: 执行一个可能失败的核心操作 page.goto(“/unstable-api-page”) # 使用更严格的等待和检查 page.wait_for_selector(“#data-table”, state=“visible”, timeout=30000) # 如果成功,跳出循环 print(f“第{attempt}次尝试成功”) break except Exception as e: print(f“第{attempt}次尝试失败,错误: {e}”) if attempt == max_retries: # 最后一次尝试也失败,记录错误并可能触发警报 log_error_to_system(e) raise # 重新抛出异常 else: # 重试前,尝试恢复状态(如回到首页、清除异常弹窗) page.goto(“/home”) # 可选:等待一段时间再重试 page.wait_for_timeout(5000)关键点:
- 明确的重试机制:对于网络超时、元素短暂不可见等瞬时错误,重试是有效的。
- 状态恢复:重试前,尽量将浏览器状态恢复到一个已知的“干净”起点,避免错误累积。
- 最终失败处理:重试耗尽后,必须有明确的失败处理路径,如记录详细日志、发送通知、保存错误截图等。
6.4 集成到CI/CD与任务调度
自动化脚本最终需要无人值守地运行。
CI/CD集成(如GitLab CI, Jenkins):
# .gitlab-ci.yml 示例 stages: - test - rpa e2e-tests: stage: test image: mcr.microsoft.com/playwright/python:v1.40.0-focal script: - pip install -r requirements.txt - playwright install chromium - pytest tests/ --html=report.html --self-contained-html artifacts: paths: - report.html - downloads/ # 如果有下载文件 when: always nightly-rpa: stage: rpa image: mcr.microsoft.com/playwright/python:v1.40.0-focal script: - pip install -r requirements.txt - playwright install chromium - python run_rpa_flow.py # 一个调用核心RPA流程的入口脚本 only: - schedules # 仅由计划任务触发使用Docker镜像可以确保环境一致性。
mcr.microsoft.com/playwright/python是官方镜像,包含了Python、Playwright和所有浏览器依赖。本地任务调度(如Windows Task Scheduler, cron):
- 创建一个批处理文件(
.bat)或Shell脚本(.sh),在其中激活虚拟环境并运行pytest命令或Python主脚本。 - 在系统任务计划程序中配置定时执行此脚本。
- 创建一个批处理文件(
7. 调试、报告与问题排查实战
即使是最好的脚本也会出错。一套高效的调试和问题排查流程至关重要。
7.1 利用Playwright强大的调试工具
录制功能:对于不熟悉的页面操作,可以使用Playwright CodeGen快速生成脚本框架。
playwright codegen https://example.com这会在浏览器中打开一个可交互的录制界面,你的操作会被实时转换成Python代码。
追踪查看器(Trace Viewer):这是Playwright的“时光机”。在测试失败时自动记录追踪信息,可以事后查看每一步操作的截图、网络请求、控制台日志。
# 在conftest.py中配置失败时自动记录追踪 @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield report = outcome.get_result() if report.when == “call” and report.failed: # 获取page夹具 page = item.funcargs.get(“page”) if page: trace_path = f“./traces/{item.name}_{datetime.now().strftime(‘%Y%m%d_%H%M%S’)}.zip” page.context.tracing.stop(path=trace_path)使用
playwright show-trace trace.zip命令打开追踪文件进行可视化调试。慢动作与视频录制:在调试时,可以放慢操作速度或录制视频。
# 在夹具或代码中设置慢动作(单位毫秒) browser = p.chromium.launch(slow_mo=500) # 每个操作间隔500ms # 或者在上下文中启用视频录制 context = browser.new_context(record_video_dir=“./videos/”)
7.2 生成丰富的测试报告
pytest-html插件生成HTML报告是基本操作。为了更直观,可以集成pytest-allure生成更美观的Allure报告,它支持附件(截图、追踪文件),非常适合团队分享。
pip install allure-pytest # 运行测试并生成Allure结果数据 pytest --alluredir=./allure-results # 生成并打开HTML报告 allure serve ./allure-results在代码中,可以动态添加附件到报告中:
import allure from playwright.sync_api import Page def test_with_attachments(page: Page): page.goto(“https://example.com”) # 在失败时自动截图是配置的,也可以手动添加 allure.attach( page.screenshot(full_page=True), name=“首页截图”, attachment_type=allure.attachment_type.PNG ) # 也可以附加页面HTML或文本日志 allure.attach(page.content(), name=“页面源码”, attachment_type=allure.attachment_type.HTML)7.3 常见问题排查清单
以下是我在实践中总结的“高频踩坑点”:
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 元素找不到 (TimeoutError) | 1. 选择器错误或元素尚未加载。 2. 元素在iframe或shadow DOM内。 3. 页面有动态ID或类名。 | 1. 使用Playwright Inspector (playwright codegen) 重新定位元素,优先用get_by_role/text/label。2. 使用 page.frame_locator()定位iframe内元素,用locator.evaluate_handle(‘elem => elem.shadowRoot’)处理shadow DOM。3. 使用包含部分文本或属性的选择器,如 page.locator(‘div:has-text(“部分文本”)’)。 |
| 点击/输入无效 | 1. 元素被遮挡。 2. 元素状态不可交互(disabled, hidden)。 3. 需要先触发某些事件(如focus)。 | 1. 使用locator.click(force=True)强制点击(慎用)。2. 操作前用 expect(locator).to_be_enabled()或.to_be_visible()断言状态。3. 尝试先 locator.focus()再locator.fill()。 |
| 页面响应慢导致超时 | 默认超时时间(30秒)不足。 | 1. 为特定操作增加超时:locator.click(timeout=60000)。2. 全局增加超时:在 browser_context_args夹具中设置“viewport”: {“width”: 1920, “height”: 1080}, “has_touch”: false有时能加速渲染。 |
| 并行测试时相互干扰 | 测试用例间状态未隔离(共用Cookie、LocalStorage)。 | 1. 确保每个测试使用独立的BrowserContext(pytest-playwright默认的page夹具已做到)。2. 使用 browser.new_context()显式创建独立上下文,并在测试后关闭。 |
| CI环境中测试失败 | CI环境(如Docker)可能缺少库、资源不足或网络不同。 | 1. 使用官方Playwright Docker镜像。 2. 在CI配置中增加资源限制(如内存)。 3. 对网络请求增加重试和更长的超时。 4. 在失败时自动保存截图和追踪文件,用于事后分析。 |
最后的心得:稳定可靠的Web自动化,20%在于编写操作代码,80%在于处理各种边界情况、等待策略和异常恢复。不要追求一次写出完美的脚本,而应建立一个“编写-运行-失败-分析-改进”的快速反馈循环。充分利用Playwright提供的调试工具,尤其是Trace Viewer,它能帮你快速定位那些“在我机器上好好的”之类的问题根源。将你的自动化脚本也当成一个产品来对待,注重可读性、可维护性和可观测性,长期来看会节省你大量的维护成本。
