在自动化测试中,测试框架的集成能力直接影响脚本的复用性和执行效率。Playwright 提供强大的浏览器控制能力,而 Pytest 作为 Python 生态中最流行的测试框架,其参数化测试、Fixture 管理和并行执行功能可显著提升测试覆盖率和执行速度。 核心价值对比:
传统模式 | Pytest 集成模式 |
---|---|
手动编写重复测试逻辑 | 参数化驱动多场景测试 |
串行执行浏览器测试 | 多浏览器并行执行 |
缺乏统一报告和失败重试机制 | 自动生成 HTML 报告 + 失败重试 |
参数化是减少代码冗余的核心手段,Pytest 通过 @pytest.mark.parametrize
实现数据驱动测试。
import pytest
from playwright.sync_api import Page
@pytest.mark.parametrize("username, password", [
("user1", "pass1"),
("user2", "pass2"),
("admin", "admin123")
])
def test_login(page: Page, username, password):
page.goto("https://example.com/login")
page.locator("#username").fill(username)
page.locator("#password").fill(password)
page.locator("#submit").click()
assert page.url == "https://example.com/dashboard"
import csv
import pytest
def load_test_data():
with open("test_data.csv") as f:
return list(csv.reader(f))
@pytest.mark.parametrize("product, quantity", load_test_data())
def test_add_to_cart(page: Page, product, quantity):
page.locator(f"text={product}").click()
page.locator("#quantity").fill(quantity)
page.locator("#add-cart").click()
assert page.locator(".cart-count").text_content() == quantity
import pytest
from itertools import product
# 生成浏览器+分辨率组合参数
browsers = ["chromium", "firefox"]
resolutions = [(1920, 1080), (375, 812)]
@pytest.mark.parametrize("browser_type, resolution", product(browsers, resolutions))
def test_responsive(browser_type, resolution, request):
browser = request.getfixturevalue("browser")
context = browser.new_context(viewport={"width": resolution[0], "height": resolution[1]})
page = context.new_page()
page.goto("https://example.com")
assert page.locator("#header").is_visible()
@pytest.fixture(params=["chromium", "firefox", "webkit"])
def browser_type(request):
return request.param
def test_cross_browser(browser_type, playwright):
browser = getattr(playwright, browser_type).launch()
page = browser.new_page()
page.goto("https://example.com")
assert "Example" in page.title()
参数化策略选型指南:
itertools.product
通过 pytest-xdist
实现并行化,结合 Playwright 的浏览器上下文隔离机制。
from playwright.sync_api import sync_playwright
def test_parallel_tabs():
with sync_playwright() as p:
browser = p.chromium.launch()
context = browser.new_context()
# 创建两个独立页面
page1 = context.new_page()
page2 = context.new_page()
# 并行操作
page1.goto("https://example.com/login")
page2.goto("https://example.com/shop")
assert page1.title() == "Login"
assert"Products"in page2.content()
[pytest]
addopts = -n auto # 自动启用CPU核心数并行
import pytest
from playwright.async_api import async_playwright
@pytest.mark.asyncio
asyncdef test_async_parallel():
asyncwith async_playwright() as p:
# 同时启动两个浏览器实例
browser1 = await p.chromium.launch()
browser2 = await p.firefox.launch()
page1 = await browser1.new_page()
page2 = await browser2.new_page()
await asyncio.gather(
page1.goto("https://example.com"),
page2.goto("https://example.com")
)
assertawait page1.title() == await page2.title()
# conftest.py 配置多浏览器 Fixture
@pytest.fixture(params=[
{"browser": "chromium", "headless": True},
{"browser": "firefox", "headless": False},
{"browser": "webkit", "device": "iPhone 13"}
], ids=["Chromium-headless", "Firefox-UI", "WebKit-iOS"])
def browser_config(request):
return request.param
def test_config_driven(browser_config, playwright):
browser_type = getattr(playwright, browser_config["browser"])
browser = browser_type.launch(headless=browser_config.get("headless", True))
context = browser.new_context(**browser_config)
# ...
并行优化技巧:
context
而非 browser
作为隔离单位,减少资源占用-n 4
限制并行进程数,防止内存溢出pytest
的 setup/teardown
重置状态@pytest.fixture(autouse=True)
def clean_db():
# 测试前清空测试数据库
reset_test_database()
yield
# 测试后回滚变更
rollback_transactions()
::placeholder
)# 推荐:使用面向用户的定位器
page.get_by_role("button", name="Submit").click() # 兼容所有浏览器
pytest-sugar
美化输出,或添加进程标识符# 日志中显示进程ID
import os
print(f"[PID-{os.getpid()}] Opening page: {url}")
e2e/
├── conftest.py # Pytest 全局 Fixture
├── test_login.py # 登录模块测试
├── test_cart.py # 购物车测试
└── browsers.py # 浏览器配置矩阵
import pytest
from playwright.sync_api import sync_playwright
@pytest.fixture(scope="session")
def playwright():
with sync_playwright() as p:
yield p
@pytest.fixture(params=["chromium", "firefox"], scope="function")
def browser(playwright, request):
browser = getattr(playwright, request.param).launch()
yield browser
browser.close()
@pytest.fixture
def page(browser):
context = browser.new_context(viewport={"width": 1280, "height": 720})
page = context.new_page()
yield page
context.close()
import pytest
PRODUCTS = ["Laptop", "Phone", "Headphones"]
@pytest.mark.parametrize("product", PRODUCTS)
def test_add_product(page, product):
page.goto(f"https://shop.com/search?q={product}")
page.locator(f"text={product}").first.click()
page.locator("#add-to-cart").click()
assert page.locator(".cart-notify").contains_text("Added")
# 多用户并发加购测试
@pytest.mark.parametrize("user", ["user1", "user2"])
def test_concurrent_cart(page, user):
login(page, user) # 登录逻辑封装
add_random_product(page)
assert page.locator(".cart-count").text_content() > "0"
pytest_generate_tests
钩子动态生成参数pytest-xdist
分配测试给多个 Workerbrowser
Fixture 用 scope="session"
减少启动开销pytest-monitor
分析内存/CPU 瓶颈pytest-html
+ playwright-trace
生成带录屏的报告pytest-rerunfailures
自动重试 flaky 测试def test_login(page: Page)
)提升可读性终极组合建议:
掌握此技术栈,可构建 日均执行 10,000+ 测试用例的企业级框架。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。