第 6 章 · pytest 集成与 fixture 体系
本章目标:装好官方 pytest 插件,掌握 page/context/browser 等 fixtures 的作用域与隔离语义,学会用命令行选项和 conftest 定制浏览器行为。
6.1 从"手写脚本"到 pytest 插件
前几章的脚本要自己管 launch/close。官方插件 pytest-playwright 把这些全部接管:
pip install pytest-playwright
playwright install # 首次需要下载内核写测试只需声明 page fixture,其余全自动:
# test_example.py
import re
from playwright.sync_api import Page, expect
def test_has_title(page: Page):
page.goto("https://playwright.dev/")
expect(page).to_have_title(re.compile("Playwright"))
def test_get_started_link(page: Page):
page.goto("https://playwright.dev/")
page.get_by_role("link", name="Get started").click()
expect(page.get_by_role("heading", name="Installation")).to_be_visible()pytest # 默认 chromium 无头
pytest --browser firefox # 换内核
pytest --headed --slowmo 500 # 有头慢放调试6.2 测试隔离:为什么每个测试都有新 context
插件的隔离策略是:每个测试函数获得一个全新的 BrowserContext(相当于全新无痕档案),测试结束自动销毁。这等价于 beforeEach 里"清空 Cookie + localStorage + sessionStorage":
def test_a(page: Page):
page.goto("https://example.com")
# 此处设置的登录态、localStorage 只属于 test_a
def test_b(page: Page):
page.goto("https://example.com")
# 完全干净的环境,绝不会被 test_a 污染而 browser / playwright 是 session 级fixture:整个会话共用一个浏览器进程(启动贵),context/page 则是 function 级(隔离便宜)。这正是第 2 章对象模型的应用:贵的共享,便宜的隔离。
6.3 Fixture 清单与作用域
| Fixture | 作用域 | 说明 |
|---|---|---|
page | function | 本测试专属页面 |
context | function | 本测试专属 context |
new_context | function | 手动再开额外 context(多用户场景) |
playwright | session | Playwright 实例 |
browser | session | 当前浏览器实例 |
browser_name / browser_channel | session | 当前内核名字/渠道字符串 |
is_chromium / is_firefox / is_webkit | session | 内核判断布尔值 |
CLI 参数只作用于默认 fixtures
--browser 等选项只影响插件提供的默认 browser/context/page。你用 browser.new_context() 自建的 context 不会继承这些参数。
6.4 命令行选项速查
pytest --browser chromium --browser firefox --browser webkit # 多内核矩阵
pytest --browser-channel chrome # 用系统 Chrome 而非捆绑内核
pytest --device "iPhone 13" # 设备仿真
pytest --tracing retain-on-failure # 失败时留 trace(第 11 章)
pytest --screenshot only-on-failure # 失败自动截图
pytest --video retain-on-failure # 失败留视频
pytest --output my-artifacts # 工件输出目录常用选项可以固化到 pytest.ini:
[pytest]
addopts = --headed --browser firefox --screenshot only-on-failure6.5 conftest 定制:browser_context_args 与自定义 fixture
全局修改 context 选项(视口、时区、忽略 HTTPS 错误):
# conftest.py
import pytest
@pytest.fixture(scope="session")
def browser_context_args(browser_context_args):
return {
**browser_context_args,
"viewport": {"width": 1920, "height": 1080},
"locale": "zh-CN",
"ignore_https_errors": True,
}单个测试临时覆盖用 marker:
@pytest.mark.browser_context_args(timezone_id="Europe/Berlin")
def test_berlin_timezone(page):
...实战中最常见的定制是"带登录态的 page"——先在模块级登录一次,把 storage_state 存下来供后续复用(完整方案见第 9 章):
@pytest.fixture(scope="module")
def auth_page(browser, base_url):
context = browser.new_context()
page = context.new_page()
page.goto(f"{base_url}/login")
page.get_by_label("用户名").fill("admin")
page.get_by_label("密码").fill("secret")
page.get_by_role("button", name="登录").click()
expect(page).to_have_url(re.compile(".*/dashboard"))
yield page # module 内所有测试共享这个已登录页面
context.close()配合 pytest-base-url 插件,page.goto("/admin") 会自动拼上 --base-url:
pytest --base-url http://localhost:8080按内核跳过或限定:
@pytest.mark.skip_browser("firefox") # firefox 下跳过
@pytest.mark.only_browser("chromium") # 仅 chromium 运行6.6 本章小结
pip install pytest-playwright后声明page即可开写,launch/close 全托管;- 隔离语义:page/context 每测一个(干净档案),browser/playwright 整场一个;
- CLI 参数只作用于默认 fixtures;自建 context 不受影响;
browser_context_argsfixture 全局定制 context,marker 单测覆盖;--tracing/--screenshot/--video retain-on-failure是 CI 失败排查的三件套。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. pytest-playwright 中 page fixture 的隔离级别是?
2. 执行 pytest --browser firefox 后,browser.new_context() 新建的 context 是什么内核?
3. 想把所有测试的 viewport 改成 1920x1080,最合适的做法是?
4. 关于 --tracing retain-on-failure,说法正确的是?
🛠️ 动手实践
- 写两个测试:第一个往 localStorage 写入数据,第二个读取并断言为空,验证 context 级隔离确实生效。
- 配置
pytest.ini固化addopts = --browser firefox --screenshot only-on-failure,故意写个失败测试,查看 test-results 目录产物。 - 在 conftest 中定制
browser_context_args设置中文 locale 和移动端 viewport,访问任意站点打印navigator.userAgent验证生效。