1. 项目概述:为什么是Playwright?
如果你正在寻找一个能真正“一统江湖”的Web自动化测试工具,那么Playwright绝对是你绕不开的名字。我接触过Selenium、Puppeteer,也折腾过Cypress,但最终让我在团队中力推并大规模应用的,还是Playwright。它不是一个简单的工具升级,而是一次测试理念的革新。简单来说,Playwright是一个由微软开源的Node.js库,同时提供了Python、Java和.NET的绑定,它允许你用一套代码,同时驱动Chromium、Firefox和WebKit(Safari的引擎)三大浏览器进行自动化操作。这意味着,你写一次测试脚本,就能覆盖市面上几乎所有主流浏览器内核,这对于追求测试覆盖率和效率的团队来说,简直是降维打击。
为什么说它是“终极实战指南”级别的方案?因为在实际项目中,我们遇到的痛点远不止“点击按钮”和“输入文本”。页面加载慢导致元素定位失败、动态内容异步加载、iframe嵌套、文件上传下载、网络请求拦截与模拟、甚至是移动端设备模拟……这些才是真正的拦路虎。Playwright在设计之初就考虑了这些生产环境中的复杂场景,提供了开箱即用的强大API。比如,它的自动等待机制能智能等待元素可操作,大大减少了编写显式等待(time.sleep)的繁琐和脆弱性;再比如,它原生支持录制生成代码,对新手极其友好。本指南的目的,就是带你从零开始,搭建一套基于Playwright Python的、可用于真实项目的、健壮的跨浏览器自动化测试解决方案,避开我踩过的所有坑,直达最佳实践。
2. 环境搭建与核心配置详解
工欲善其事,必先利其器。一个稳定、可复现的测试环境是自动化成功的基石。这里我会详细拆解每一步,并解释其背后的原因。
2.1 Python环境与Playwright安装
首先,确保你有一个干净的Python环境。我强烈建议使用venv或conda创建独立的虚拟环境,以避免包依赖冲突。这是血泪教训,曾经因为全局环境混乱,导致不同项目间的Playwright版本和浏览器版本打架,排查了整整一天。
# 创建并激活虚拟环境 (以venv为例) python -m venv playwright-env # Windows playwright-env\Scripts\activate # macOS/Linux source playwright-env/bin/activate接下来安装Playwright的Python包。这里有个关键选择:是只安装核心库,还是连同浏览器一起安装?对于团队协作或CI/CD环境,我推荐分步安装。
# 安装playwright核心库 pip install playwright # 安装浏览器(Chromium, Firefox, WebKit)。这一步会下载浏览器二进制文件,体积较大。 playwright install注意:
playwright install命令默认会安装所有支持浏览器(chromium, firefox, webkit)的稳定版本。如果你只需要特定浏览器,可以使用playwright install chromium。在国内网络环境下,这一步可能会很慢或失败。解决方案是配置镜像源:设置环境变量PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright后再执行安装命令,速度会快很多。这是解决“安装浏览器慢”这个热搜问题的关键技巧。
2.2 项目结构与配置管理
一个混乱的脚本目录是维护的噩梦。从第一天起,就要建立清晰的项目结构。这是我的推荐结构:
your-automation-project/ ├── config/ # 配置文件 │ ├── __init__.py │ ├── settings.py # 全局配置(超时时间、基础URL等) │ └── browsers.py # 浏览器启动配置 ├── pages/ # 页面对象模型(Page Object Model, POM) │ ├── __init__.py │ ├── login_page.py │ └── home_page.py ├── tests/ # 测试用例 │ ├── __init__.py │ ├── conftest.py # Pytest fixtures 配置(核心!) │ ├── test_login.py │ └── test_search.py ├── fixtures/ # 测试数据 │ └── test_data.json ├── reports/ # 测试报告(自动生成) ├── utils/ # 工具函数(如截图、日志) │ └── helper.py ├── requirements.txt # 项目依赖 └── pytest.ini # Pytest 配置文件为什么采用POM(页面对象模型)?它将页面的元素定位和操作封装成类,测试脚本只调用业务方法。当页面UI变动时,你只需要修改对应的Page类,而不是散落在各处的测试脚本,极大提升了可维护性。conftest.py是Pytest的精华,我们可以在这里定义全局的browser和pagefixture,让所有测试用例共享一套浏览器上下文配置。
2.3 核心配置代码解析
让我们深入config/settings.py和conftest.py,看看如何配置一个健壮的测试环境。
config/settings.py:
import os from pathlib import Path BASE_URL = "https://your-test-site.com" DEFAULT_TIMEOUT = 30000 # Playwright默认超时是30秒,这里显式定义便于调整 HEADLESS = os.getenv("HEADLESS", "True").lower() == "true" # 环境变量控制是否无头模式 SLOW_MO = int(os.getenv("SLOW_MO", 100)) # 操作延迟(毫秒),调试时非常有用 SCREENSHOT_ON_FAILURE = True REPORT_PATH = Path(__file__).parent.parent / "reports"tests/conftest.py(核心Fixture定义):
import pytest import allure from playwright.sync_api import Browser, BrowserContext, Page from config.settings import HEADLESS, SLOW_MO, SCREENSHOT_ON_FAILURE, REPORT_PATH @pytest.fixture(scope="session") def browser(browser_type_launch_args): """启动一个浏览器实例,整个测试会话只启动一次""" # browser_type_launch_args 是Playwright pytest插件提供的fixture,可获取浏览器类型 browser = browser_type_launch_args.launch(headless=HEADLESS, slow_mo=SLOW_MO) yield browser browser.close() @pytest.fixture def context(browser: Browser, browser_context_args): """为每个测试用例创建一个独立的浏览器上下文(类似隐身会话)""" # browser_context_args 可以传递上下文参数,如视口大小、权限等 context = browser.new_context(**browser_context_args) yield context context.close() @pytest.fixture def page(context: BrowserContext) -> Page: """为每个测试用例创建一个独立的页面(Tab)""" page = context.new_page() yield page if SCREENSHOT_ON_FAILURE: # 如果测试失败,自动截图并附加到Allure报告 if hasattr(pytest, “test_failed”) and pytest.test_failed: screenshot_path = REPORT_PATH / f"screenshot_{pytest.current_test_name}.png" page.screenshot(path=screenshot_path) allure.attach.file(screenshot_path, name="失败截图", attachment_type=allure.attachment_type.PNG) page.close()这里的关键点在于browser,context,page三个fixture的作用域(scope):
browser(session): 启动浏览器进程,成本高,整个测试套件共用。context(function): 每个测试用例一个独立的上下文,实现了测试间的完全隔离,避免cookie、localStorage相互污染。page(function): 每个测试用例一个标签页,是最常用的操作对象。
使用browser_context_argsfixture(需要安装pytest-playwright插件)可以方便地在pytest.ini或命令行中配置上下文参数,如模拟移动设备:
# pytest.ini [pytest] addopts = --browser chromium --browser firefox --browser webkit --headed # 覆盖HEADLESS设置,有头模式运行 --slowmo 100 --device “iPhone 12”3. 核心API与最佳实践模式
掌握了环境配置,我们来深入Playwright Python API的核心。很多人学了API但写出的脚本依然脆弱,问题往往出在没有遵循正确的模式。
3.1 元素定位与等待:告别time.sleep
Playwright最令人称道的特性之一是其强大的自动等待。它会在执行操作(如click,fill)前,自动等待元素满足可操作条件(如可见、可点击、稳定)。这意味着在绝大多数情况下,你不需要手动写page.wait_for_selector。
最佳定位策略(按优先级排序):
- Role-based 定位 (
get_by_role):这是首选。通过元素的ARIA角色(如button、textbox)和可访问名(name)来定位。这最接近用户感知方式,且对UI变化最不敏感。# 优于 page.locator(“button.submit”) page.get_by_role(“button”, name=“提交”).click() - Text-based 定位 (
get_by_text,get_by_label):根据可见文本或标签文本来定位。非常直观。page.get_by_text(“登录”).click() page.get_by_label(“用户名”).fill(“admin”) - Test ID 定位 (
get_by_test_id):与开发约定,为关键测试元素添加专用的># 前端元素:<button># CSS page.locator(“.primary-btn:has-text(‘确认’)”).click() # XPath (不得已时使用) page.locator(“//button[contains(@class, ‘submit’)]”).click()
显式等待的使用场景:虽然自动等待很强大,但在某些特定场景仍需显式等待:
- 等待网络请求完成:
page.wait_for_response(url_pattern) - 等待导航完成:
page.wait_for_url(url) - 等待元素达到特定状态:
page.locator(“#progress”).wait_for(state=“hidden”) - 等待自定义条件:
page.wait_for_function(“window.appState === ‘ready’”)
实操心得:永远不要使用
time.sleep!这是自动化测试的“癌症”。它会让测试变得极慢且不可靠(因为网络或机器性能差异)。利用Playwright的内置等待,让你的测试既快速又健壮。
3.2 页面对象模型(POM)的现代化实现
传统的POM可能只是简单的元素定位器集合。我们可以利用Python的@property装饰器和Playwright的Locator对象,实现更优雅、更强大的POM。
pages/login_page.py:
from playwright.sync_api import Page from config.settings import BASE_URL class LoginPage: def __init__(self, page: Page): self.page = page self._endpoint = “/login” @property def url(self): return BASE_URL + self._endpoint # 使用property将定位器定义为属性,延迟计算,更清晰 @property def username_input(self): return self.page.get_by_label(“用户名或邮箱”) @property def password_input(self): return self.page.get_by_label(“密码”) @property def submit_button(self): return self.page.get_by_role(“button”, name=“登录”) @property def error_message(self): return self.page.locator(“.alert-error”) # 页面操作方法 def navigate(self): self.page.goto(self.url) return self def login(self, username: str, password: str): """登录核心业务流""" self.navigate() self.username_input.fill(username) self.password_input.fill(password) self.submit_button.click() # 可以在这里加入等待登录成功的逻辑,比如等待跳转或某个元素出现 self.page.wait_for_url(**contains(“/dashboard”)) return DashboardPage(self.page) # 返回下一个页面对象,实现链式调用 def get_error_text(self) -> str: return self.error_message.text_content() if self.error_message.is_visible() else “”在测试用例中使用这个Page对象,代码会非常清晰:
def test_successful_login(page): login_page = LoginPage(page) dashboard_page = login_page.login(“valid_user”, “valid_pass”) # 断言登录成功,例如检查dashboard页面是否有用户菜单 assert dashboard_page.user_menu.is_visible()这种模式的优点在于,将元素定位、页面操作和业务逻辑完美分离。测试用例读起来就像自然语言描述的验收标准。
3.3 处理复杂交互:文件、iframe、弹窗与API拦截
文件上传与下载:Playwright处理文件上传极其简单,无需像Selenium那样找input元素并send_keys。
# 文件上传 - 直接设置输入文件 page.locator(“input[type=‘file’]”).set_input_files(‘my-file.pdf’) # 上传多个文件 page.locator(“input[type=‘file’]”).set_input_files([‘file1.pdf’, ‘file2.jpg’]) # 等待文件上传完成(监听特定请求) with page.expect_response(lambda response: “/upload” in response.url): page.click(“#upload-button”) # 文件下载 - 监听下载事件 with page.expect_download() as download_info: page.click(“a#download-link”) download = download_info.value # 指定下载路径 path = download.save_as(“/path/to/save.pdf”)处理iframe:定位iframe内的元素需要先获取Frame对象。
# 通过名称或URL定位iframe frame = page.frame(name=“widget-frame”) # 或 page.frame(url=re.compile(r”.*widget.*”)) # 然后在frame对象上操作 frame.fill(“#input-inside-iframe”, “text”) # 更简洁的方式:使用frame_locator page.frame_locator(“iframe[name=‘widget-frame’]”).locator(“button”).click()处理弹窗(对话框):Playwright可以监听并接受或拒绝原生的alert,confirm,prompt。
# 监听并接受confirm对话框 page.on(“dialog”, lambda dialog: dialog.accept()) page.click(“button-that-triggers-confirm”) # 更精确的控制:使用 expect_event page.once(“dialog”, lambda dialog: dialog.accept(“输入的文字”)) page.click(“button-that-triggers-prompt”)网络请求拦截与模拟(Mock):这是Playwright的王牌功能之一,用于测试边缘场景或屏蔽不稳定第三方依赖。
# 1. 拦截并修改请求 page.route(“**/api/user”, lambda route: route.fulfill( status=200, content_type=“application/json”, body=json.dumps({“name”: “Mock User”, “id”: 123}) )) # 之后所有匹配的API请求都会返回这个模拟数据 # 2. 拦截并继续请求(如修改请求头) page.route(“**/*”, lambda route: route.continue_(headers={**route.request.headers, “x-test”: “true”})) # 3. 记录或断言网络请求 def log_request(request): print(f“{request.method} {request.url}”) page.on(“request”, log_request) # 等待特定请求并获取其响应 response = page.wait_for_response(“**/api/data”) data = response.json() assert data[“status”] == “success”4. 跨浏览器测试与多环境执行策略
“跨浏览器”不是简单地在不同浏览器里跑同一套脚本。不同浏览器有细微的渲染、行为和性能差异,需要有针对性的策略。
4.1 配置与启动多浏览器
在conftest.py中,我们可以通过参数化(pytest.mark.parametrize)或Pytest插件来运行多浏览器测试。
方法一:使用pytest-playwright插件(推荐)安装插件:pip install pytest-playwright。它提供了browser_type_launch_args和browser_context_args等fixture,以及命令行参数。
# 在Chromium和Firefox上运行所有测试 pytest --browser chromium --browser firefox # 指定有头模式运行,便于调试 pytest --browser chromium --headed方法二:自定义参数化fixture如果你需要更精细的控制,可以在conftest.py中定义:
import pytest from playwright.sync_api import BrowserType @pytest.fixture(params=[“chromium”, “firefox”, “webkit”]) def browser_type(request, playwright): yield getattr(playwright, request.param) @pytest.fixture def browser(browser_type: BrowserType): browser = browser_type.launch(headless=False) yield browser browser.close()这样,每个测试用例都会自动在三个浏览器上各运行一次。
4.2 处理浏览器特异性问题
尽管Playwright尽力统一了API,但差异依然存在。你需要一个策略来应对。
条件执行与跳过:对于已知的、仅在特定浏览器上存在的问题或尚未支持的功能,使用条件跳过。
import pytest from config.settings import BROWSER_NAME # 从环境变量获取当前浏览器 @pytest.mark.skipif(BROWSER_NAME == “webkit”, reason=“WebKit下文件下载API行为不同,暂不测试”) def test_file_download(page): # ... 测试代码 # 或者,在测试内部进行条件判断 def test_geolocation(page): if page.context.browser.browser_type.name == ‘firefox’: pytest.skip(“Firefox在此版本中地理位置权限需要额外配置”) # ... 其他浏览器的测试代码浏览器特定的定位器或操作:极少数情况下,你可能需要为不同浏览器准备不同的定位器。
def get_submit_button(page): if page.context.browser.browser_type.name == ‘webkit’: return page.locator(“css=.safari-special-btn”) else: return page.get_by_role(“button”, name=“提交”)视觉回归测试:使用
page.screenshot()进行截图,并配合像pixelmatch这样的库进行图片对比,捕捉不同浏览器间的渲染差异。这通常是UI测试的最后一环。
4.3 集成CI/CD与并行执行
真正的实战方案必须能在CI/CD流水线中快速、稳定地运行。核心是并行化。
使用Pytest-xdist进行并行测试:
pip install pytest-xdist # 启动4个worker并行执行测试 pytest -n auto # ‘auto’会根据CPU核心数自动设置worker数 pytest -n 4 --browser chromium --browser firefox这会让测试套件执行时间成倍缩短。注意,并行测试时,要确保测试用例之间是独立的,不能有共享状态依赖。我们之前用context和pagefixture为每个测试创建独立环境,正是为此做准备。
CI配置示例(GitHub Actions):
name: Playwright Tests on: [push] jobs: test: timeout-minutes: 60 runs-on: ubuntu-latest strategy: fail-fast: false # 一个浏览器失败不影响其他浏览器继续测试 matrix: browser: [chromium, firefox, webkit] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: ‘3.10’ - name: Cache Playwright browsers uses: actions/cache@v3 with: path: ~/.cache/ms-playwright key: ${{ runner.os }}-playwright-${{ hashFiles(‘**/package-lock.json’, ‘**/poetry.lock’) }} restore-keys: | ${{ runner.os }}-playwright- - name: Install dependencies run: | pip install -r requirements.txt playwright install --with-deps ${{ matrix.browser }} # 只安装当前测试需要的浏览器 - name: Run your tests run: | pytest tests/ --browser ${{ matrix.browser }} --headless --junitxml=results-${{ matrix.browser }}.xml - name: Upload test results if: always() uses: actions/upload-artifact@v3 with: name: test-results-${{ matrix.browser }} path: results-${{ matrix.browser }}.xml这个配置实现了:1) 缓存浏览器二进制文件以加速构建;2) 矩阵策略在多浏览器上并行运行测试;3) 生成JUnit格式报告用于CI集成。
5. 高级技巧、调试与问题排查
即使遵循了最佳实践,在复杂的Web应用中你依然会遇到各种诡异的问题。这一章分享我积累的“救命”技巧。
5.1 调试技巧大全
- 有头模式与慢动作:这是最基本的调试手段。在命令行添加
--headed和--slowmo 1000(1000毫秒),让你可以清晰看到每一步操作。 - Playwright Inspector:这是官方神器。通过设置环境变量
PWDEBUG=1运行脚本,会自动打开一个调试器,你可以单步执行、查看页面快照、检查定位器,甚至能直接修改脚本并重新执行。PWDEBUG=1 pytest test_login.py -k “test_failed_login” --headed - 录制代码:对于不熟悉的页面,先用
playwright codegen录制操作生成代码骨架,再在其基础上修改和完善。这是快速上手的神器。playwright codegen https://your-test-site.com - 丰富的截图与录屏:
# 截图 page.screenshot(path=“fullpage.png”, full_page=True) # 截取整个可滚动页面 page.locator(“#widget”).screenshot(path=“widget.png”) # 截取特定元素 # 录屏(需要以context方式启动) context = browser.new_context(record_video_dir=“videos/”) page = context.new_page() # ... 测试操作 ... # 测试结束后,视频会自动保存 - 控制台日志与网络监听:
# 监听控制台日志 page.on(“console”, lambda msg: print(f“CONSOLE: {msg.type}: {msg.text}”)) # 监听页面错误 page.on(“pageerror”, lambda error: print(f“PAGE ERROR: {error}”)) # 监听所有网络请求和响应(调试时非常有用,但生产环境慎用,日志量巨大) # page.on(“request”, lambda req: print(f“>> {req.method} {req.url}”)) # page.on(“response”, lambda res: print(f“<< {res.status} {res.url}”))
5.2 常见问题排查清单
当你遇到元素找不到、点击没反应、测试不稳定等问题时,按这个清单排查:
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
Locator.click()超时 | 1. 元素被遮挡(弹窗、遮罩层) 2. 元素不可见( display: none,visibility: hidden)3. 元素不可交互( disabled属性)4. 坐标点错误(动态布局) | 1. 使用page.pause()或Inspector检查元素状态。2. 尝试使用 force=True参数强制点击(模拟用户绕过UI检查,慎用)。3. 改用 element_handle.click()并指定坐标。 |
Locator.fill()不输入文本 | 1. 输入框不是真正的<input>或<textarea>(如div模拟)2. 有JS事件拦截输入 | 1. 先click()聚焦,再fill()。2. 使用 page.type(selector, text, delay=100)模拟真人输入。3. 使用 page.evaluate()直接设置元素的value属性。 |
| 页面跳转后操作失效 | 1. 页面未加载完成就执行操作 2. 跳转到了新窗口或新Tab | 1. 在跳转操作后使用page.wait_for_load_state(‘networkidle’)。2. 使用 page.wait_for_event(‘popup’)或context.pages来获取新页面对象。 |
| 测试在CI上失败,本地却成功 | 1. 环境差异(浏览器版本、屏幕分辨率) 2. 网络延迟或超时设置过短 3. 资源加载失败(CDN、第三方库) | 1. 在CI日志中启用详细日志和截图。 2. 增加全局超时 DEFAULT_TIMEOUT。3. 使用 page.route拦截并Mock不稳定的第三方资源。 |
| 文件上传失败 | 1. 上传组件是自定义的,非原生<input type=“file”>2. 文件选择对话框是操作系统原生窗口 | 1. 使用page.locator(‘.upload-area’).set_input_files()直接设置文件路径(Playwright可以绕过文件选择框)。2. 如果组件通过拖拽上传,使用 page.dispatch_event(selector, ‘drop’, { dataTransfer: { files: [file] } })。 |
| iframe内元素无法定位 | 1. iframe未加载完成 2. iframe有跨域限制 3. 定位器作用域错误 | 1. 使用page.frame(…).wait_for_load_state()。2. 检查浏览器控制台是否有跨域错误,可能需要启动参数 --disable-web-security(仅测试环境)。3. 确保在 Frame对象或frame_locator上调用定位器,而不是在父page上。 |
5.3 性能优化与稳定性提升
复用Browser Context:虽然每个测试用例用独立的Context利于隔离,但创建Context有开销。对于一组紧密相关、需要共享登录态的测试,可以共享一个Context,但要在每个测试后清理Cookies和Storage (
context.clear_cookies())。合理配置超时:全局超时(
DEFAULT_TIMEOUT)设得太短会导致不必要的失败,太长会拖慢测试套件。我的经验是,根据应用响应速度,设置在20-60秒。对于特定的慢操作,可以单独设置更长的超时:locator.click(timeout=60000)。选择性安装浏览器:在CI环境中,如果只测试Chromium,就只安装Chromium,可以显著减少流水线时间和存储空间。
使用请求拦截减少不必要流量:在测试开始时,拦截并阻断所有非必要的资源请求(如图片、字体、分析脚本),可以大幅提升测试执行速度。
def block_aggressively(route): if route.request.resource_type in [“image”, “stylesheet”, “font”, “media”]: route.abort() else: route.continue_() page.route(“**/*”, block_aggressively)定期清理与维护:浏览器缓存、用户数据目录会不断增长。定期在CI脚本中添加清理步骤,并考虑使用Docker容器提供纯净的测试环境。
6. 测试报告、日志与持续集成
自动化测试如果不产生清晰的结果报告,其价值就大打折扣。我们需要让失败的原因一目了然。
6.1 生成丰富的测试报告
Allure报告:这是目前最强大、最美观的测试报告框架之一。
- 安装:
pip install allure-pytest - 在测试中添加注解和附件:
import allure import pytest @allure.title(“验证用户登录功能”) @allure.feature(“用户认证”) def test_login(page): with allure.step(“导航到登录页面”): page.goto(“/login”) with allure.step(“输入用户名和密码”): page.fill(“#username”, “test”) page.fill(“#password”, “pass”) with allure.step(“点击登录按钮”): page.click(“button[type=‘submit’]”) with allure.step(“验证登录成功”): assert page.inner_text(“.welcome”) == “Welcome, test!” # 失败时自动附加截图(已在conftest.py的page fixture中实现) - 运行测试并生成报告:
pytest tests/ --alluredir=./allure-results allure serve ./allure-results # 本地查看 allure generate ./allure-results -o ./allure-report --clean # 生成静态HTML报告
HTML报告:使用pytest-html可以快速生成简洁的HTML报告。
pip install pytest-html pytest tests/ --html=report.html --self-contained-html6.2 结构化日志记录
使用Python标准的logging模块,为测试框架添加日志。
# utils/logger.py import logging import sys def setup_logger(name): logger = logging.getLogger(name) logger.setLevel(logging.DEBUG) # 控制台处理器 ch = logging.StreamHandler(sys.stdout) ch.setLevel(logging.INFO) # 文件处理器 fh = logging.FileHandler(“automation.log”) fh.setLevel(logging.DEBUG) # 格式 formatter = logging.Formatter(‘%(asctime)s - %(name)s - %(levelname)s - %(message)s’) ch.setFormatter(formatter) fh.setFormatter(formatter) logger.addHandler(ch) logger.addHandler(fh) return logger # 在page object或测试用例中使用 logger = setup_logger(__name__) logger.info(“开始执行登录测试”) try: page.click(“button”) except Exception as e: logger.error(f“点击按钮失败: {e}”, exc_info=True)6.3 集成到CI/CD流水线
将上述所有部分组合起来,形成一个完整的CI流水线。以GitHub Actions为例,一个成熟的配置应该:
- 检出代码。
- 缓存依赖和浏览器以加速构建。
- 安装Python依赖和Playwright浏览器。
- 并行运行多浏览器测试,并生成JUnit XML格式的结果。
- 无论测试成功与否,都上传测试结果、日志、截图和录屏作为制品,便于事后分析。
- 如果测试失败,向Slack、Teams或钉钉发送通知。
# .github/workflows/playwright-ci.yml 更完整的示例 name: Playwright Cross-Browser CI on: [push, pull_request] jobs: test: runs-on: ubuntu-latest strategy: matrix: browser: [chromium, firefox] # webkit在CI上可能较慢,按需添加 python-version: [“3.9”, “3.10”] steps: - name: Checkout uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Cache dependencies uses: actions/cache@v3 with: path: | ~/.cache/pip ~/.cache/ms-playwright key: ${{ runner.os }}-py-${{ matrix.python-version }}-${{ matrix.browser }}-${{ hashFiles(‘**/requirements.txt’) }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt playwright install ${{ matrix.browser }} --with-deps - name: Run tests run: | pytest tests/ \ --browser=${{ matrix.browser }} \ --headless \ --junitxml=test-results-${{ matrix.browser }}-py${{ matrix.python-version }}.xml \ --html=report-${{ matrix.browser }}-py${{ matrix.python-version }}.html \ --self-contained-html \ -v env: HEADLESS: true - name: Upload test results if: always() uses: actions/upload-artifact@v3 with: name: test-results-py${{ matrix.python-version }}-${{ matrix.browser }} path: | test-results-*.xml report-*.html screenshots/ # 假设你的截图存于此目录 logs/ # 假设你的日志存于此目录这套方案从环境搭建、脚本编写、到执行调试、报告集成,形成了一个完整的闭环。它不仅仅是API的使用手册,更是经过真实项目锤炼后的工程化实践。记住,好的自动化测试不是一蹴而就的,而是随着项目迭代不断演进和维护的。从一个小而精的测试用例开始,逐步扩展,持续重构,你会发现Playwright Python将成为你保障Web应用质量最得力的伙伴。