Featured image of post Playwright中Fixture的高级应用技巧

Playwright中Fixture的高级应用技巧

本文我们将梳理探讨 Playwright 中内建 fixtures 的一些高级应用,帮助我们更好地使用 `Playwright`

Playwright Fixtures 进阶应用技巧

Playwright作为目前最有前景的自动化测试工具,充分吸收了很多其他框架的优点。其中很关键的就是吸收了pytest的fixture优势,在它包括js、.net 版本中也都提供了fixture的支持。 当然能在其Python 版本中也继续包含了丰富的内建 fixtures

本文我们将梳理探讨 Playwright 中内建 fixtures 的一些高级应用,帮助我们更好地使用 Playwright

Playwright 内建 Fixtures 概览

Playwright Python 版本中包含以下核心内建 fixtures:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

# 基础 fixtures
@pytest.fixture
def browser():  # 浏览器实例
    pass

@pytest.fixture
def context():  # 浏览器上下文
    pass

@pytest.fixture
def page():  # 页面实例
    pass

@pytest.fixture
def playwright():  # Playwright 实例
    pass

# 配置 fixtures
@pytest.fixture
def browser_name():  # 浏览器名称
    pass

@pytest.fixture
def browser_channel():  # 浏览器通道
    pass

@pytest.fixture
def is_mobile():  # 是否移动设备
    pass

@pytest.fixture
def device_name():  # 设备名称
    pass

@pytest.fixture
def viewport_size():  # 视口大小
    pass

# API fixtures
@pytest.fixture
def request():  # API 请求上下文
    pass

browser Fixtures 高级用法

多浏览器并行测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# test_browsers.py
import pytest
from playwright.sync_api import Browser, Page

@pytest.fixture(params=["chromium", "firefox", "webkit"])
def cross_browser_page(request, playwright) -> Page:
    """跨浏览器测试 fixture"""
    if request.param == "chromium":
        browser = playwright.chromium.launch(headless=True)
    elif request.param == "firefox":
        browser = playwright.firefox.launch(headless=True)
    else:
        browser = playwright.webkit.launch(headless=True)
    
    context = browser.new_context()
    page = context.new_page()
    
    yield page
    
    page.close()
    context.close()
    browser.close()

def test_cross_browser_compatibility(cross_browser_page):
    """跨浏览器兼容性测试"""
    cross_browser_page.goto("http://localhost:8080")
    assert cross_browser_page.title() == "Test Page"
    assert cross_browser_page.locator("h1").text_content() == "Test Page"

浏览器上下文高级配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# test_context.py
@pytest.fixture
def stealth_context(browser):
    """反检测浏览器上下文"""
    context = browser.new_context(
        viewport={"width": 1920, "height": 1080},
        user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        locale="en-US",
        timezone_id="America/New_York",
        geolocation={"longitude": -73.935242, "latitude": 40.730610},
        permissions=["geolocation", "notifications"],
        extra_http_headers={
            "Accept-Language": "en-US,en;q=0.9"
        },
        ignore_https_errors=True,
        java_script_enabled=True
    )
    
    # 注入反检测脚本
    context.add_init_script("""
        Object.defineProperty(navigator, 'webdriver', {
            get: () => undefined
        });
    """)
    
    yield context
    
    context.close()

@pytest.fixture
def stealth_page(stealth_context):
    """反检测页面"""
    page = stealth_context.new_page()
    yield page
    page.close()

def test_stealth_browser(stealth_page):
    """测试反检测浏览器"""
    stealth_page.goto("http://localhost:8080")
    
    # 检查 webdriver 属性是否被隐藏
    is_webdriver = stealth_page.evaluate("() => navigator.webdriver")
    assert is_webdriver is None
    
    # 检查用户代理
    user_agent = stealth_page.evaluate("() => navigator.userAgent")
    assert "Windows NT 10.0" in user_agent

浏览器性能监控

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# test_performance.py
@pytest.fixture
def performance_page(context):
    """性能监控页面"""
    page = context.new_context().new_page()
    
    # 启用性能监控
    metrics = []
    
    def on_metrics(metrics_data):
        metrics.append(metrics_data)
    
    page.on("metrics", on_metrics)
    
    yield page
    
    # 输出性能指标
    if metrics:
        print(f"页面性能指标: {metrics[-1]}")
    
    page.close()

def test_page_performance(performance_page):
    """页面性能测试"""
    performance_page.goto("http://localhost:8080")
    
    # 等待页面加载完成
    performance_page.wait_for_load_state("networkidle")
    
    # 获取性能指标
    metrics = performance_page.evaluate("""
        () => ({
            timing: window.performance.timing,
            memory: window.performance.memory,
            navigation: window.performance.navigation
        })
    """)
    
    # 计算页面加载时间
    load_time = metrics['timing']['loadEventEnd'] - metrics['timing']['navigationStart']
    print(f"页面加载时间: {load_time}ms")
    
    # 验证页面加载时间在合理范围内
    assert load_time < 5000  # 小于5秒

page Fixtures 高级用法

智能等待策略

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# test_smart_wait.py
@pytest.fixture
def smart_wait_page(context):
    """智能等待页面"""
    page = context.new_page()
    
    # 设置默认超时
    page.set_default_timeout(10000)
    page.set_default_navigation_timeout(30000)
    
    # 自定义等待策略
    original_wait_for_selector = page.wait_for_selector
    
    def enhanced_wait_for_selector(selector, **kwargs):
        """增强的等待选择器方法"""
        try:
            # 首先尝试快速查找
            element = page.query_selector(selector)
            if element and element.is_visible():
                return element
            
            # 如果没有找到,使用原始方法等待
            return original_wait_for_selector(selector, **kwargs)
        except Exception as e:
            print(f"等待选择器 {selector} 失败: {e}")
            raise
    
    page.wait_for_selector = enhanced_wait_for_selector
    
    yield page
    page.close()

def test_smart_waiting(smart_wait_page):
    """测试智能等待"""
    smart_wait_page.goto("http://localhost:8080")
    
    # 使用增强的等待方法
    header = smart_wait_page.wait_for_selector("h1")
    assert header.is_visible()
    assert header.text_content() == "Test Page"

网络拦截和模拟

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# test_network_mock.py
@pytest.fixture
def network_mock_page(context):
    """网络模拟页面"""
    page = context.new_page()
    
    # 拦截所有网络请求
    def handle_request(request: Request):
        if "localhost:8080/api" in request.url:
            # 模拟 API 响应
            if "/api/user" in request.url:
                request.respond({
                    "status": 200,
                    "headers": {"Content-Type": "application/json"},
                    "body": '{"status": "success", "data": {"id": 1, "name": "Mock User", "email": "mock@example.com"}}'
                })
            else:
                request.continue_()
        else:
            request.continue_()
    
    page.route("**/*", handle_request)
    
    yield page
    page.close()

def test_network_mocking(network_mock_page):
    """测试网络模拟"""
    network_mock_page.goto("http://localhost:8080")
    
    # 创建一个测试页面,包含API调用
    html_content = """
    <!DOCTYPE html>
    <html>
    <head>
        <title>Mock Test</title>
    </head>
    <body>
        <button id="fetch-btn">Fetch User</button>
        <div id="result"></div>
        <script>
            document.getElementById('fetch-btn').addEventListener('click', async () => {
                try {
                    const response = await fetch('http://localhost:8080/api/user');
                    const data = await response.json();
                    document.getElementById('result').innerHTML = 
                        `<p>Name: ${data.data.name}</p><p>Email: ${data.data.email}</p>`;
                } catch (error) {
                    document.getElementById('result').innerText = 'Error: ' + error.message;
                }
            });
        </script>
    </body>
    </html>
    """
    
    network_mock_page.set_content(html_content)
    
    # 点击按钮触发API调用
    network_mock_page.click("#fetch-btn")
    
    # 等待结果显示
    result = network_mock_page.locator("#result")
    result.wait_for(state="visible")
    
    # 验证结果
    assert "Mock User" in result.text_content()
    assert "mock@example.com" in result.text_content()

页面截图和视频录制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# test_recording.py
import os
import time

@pytest.fixture
def recording_page(context):
    """录制页面"""
    # 创建结果目录
    os.makedirs("test-results/screenshots", exist_ok=True)
    os.makedirs("test-results/videos", exist_ok=True)
    
    context = context.new_context(
        record_video_dir="test-results/videos/",
        record_video_size={"width": 1280, "height": 720}
    )
    
    page = context.new_page()
    
    yield page
    
    # 测试结束时截图
    timestamp = int(time.time())
    page.screenshot(path=f"test-results/screenshots/test_{timestamp}.png")
    
    page.close()
    context.close()

@pytest.fixture
def screenshot_on_failure_page(context):
    """失败时截图的页面"""
    page = context.new_page()
    
    yield page
    
    # 检查测试是否失败
    if hasattr(page, '_test_failed') and page._test_failed:
        timestamp = int(time.time())
        page.screenshot(path=f"test-results/screenshots/failure_{timestamp}.png")
    
    page.close()

def test_recording(recording_page):
    """测试录制功能"""
    recording_page.goto("http://localhost:8080")
    
    # 执行一些操作
    recording_page.click("h1")
    recording_page.wait_for_timeout(1000)
    
    # 验证页面内容
    assert recording_page.title() == "Test Page"

def test_screenshot_on_failure(screenshot_on_failure_page):
    """测试失败时截图"""
    # 标记测试为失败(仅用于演示)
    screenshot_on_failure_page._test_failed = True
    
    screenshot_on_failure_page.goto("http://localhost:8080")
    
    # 这个断言会失败,触发截图
    assert False, "This test is designed to fail to demonstrate screenshot on failure"

device和viewport Fixtures 高级用法

响应式测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# test_responsive.py
@pytest.fixture(params=[
    {"width": 1920, "height": 1080, "name": "Desktop"},
    {"width": 768, "height": 1024, "name": "Tablet"},
    {"width": 375, "height": 667, "name": "Mobile"}
])
def responsive_page(request, browser):
    """响应式测试页面"""
    viewport = request.param
    context = browser.new_context(viewport=viewport)
    page = context.new_page()
    
    # 添加视口信息到页面
    page._viewport_info = viewport
    
    yield page
    
    page.close()
    context.close()

def test_responsive_design(responsive_page):
    """响应式设计测试"""
    responsive_page.goto("http://localhost:8080")
    
    viewport = responsive_page._viewport_info
    
    # 根据视口大小进行不同的验证
    if viewport["name"] == "Desktop":
        # 桌面端验证
        assert responsive_page.viewport_size["width"] == 1920
        assert responsive_page.viewport_size["height"] == 1080
    elif viewport["name"] == "Tablet":
        # 平板端验证
        assert responsive_page.viewport_size["width"] == 768
        assert responsive_page.viewport_size["height"] == 1024
    elif viewport["name"] == "Mobile":
        # 移动端验证
        assert responsive_page.viewport_size["width"] == 375
        assert responsive_page.viewport_size["height"] == 667

设备模拟

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# test_device_simulation.py
@pytest.fixture(params=["iPhone 12", "iPad Pro", "Pixel 5"])
def device_page(request, playwright):
    """设备模拟页面"""
    device = playwright.devices[request.param]
    
    browser = playwright.chromium.launch(headless=True)
    context = browser.new_context(**device)
    page = context.new_page()
    
    # 添加设备信息
    page._device_info = device
    
    yield page
    
    page.close()
    context.close()
    browser.close()

def test_device_simulation(device_page):
    """设备模拟测试"""
    device_page.goto("http://localhost:8080")
    
    device = device_page._device_info
    
    # 验证用户代理
    user_agent = device_page.evaluate("() => navigator.userAgent")
    assert device["user_agent"] in user_agent
    
    # 验证视口大小
    viewport = device_page.viewport_size
    assert viewport["width"] == device["viewport"]["width"]
    assert viewport["height"] == device["viewport"]["height"]
    
    # 验证设备像素比
    device_pixel_ratio = device_page.evaluate("() => window.devicePixelRatio")
    assert device_pixel_ratio == device["device_scale_factor"]

API Fixtures 高级用法

API 测试认证

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# test_api_auth.py
@pytest.fixture
def authenticated_api_context(playwright):
    """已认证的 API 上下文"""
    api_context = playwright.request.new_context()
    
    # 登录获取 token
    login_response = api_context.post("http://localhost:8080/api/login", {
        "username": "testuser",
        "password": "testpass"
    })
    
    assert login_response.status == 200
    token = login_response.json()["token"]
    
    # 设置认证头
    api_context.set_extra_http_headers({
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    })
    
    yield api_context
    
    api_context.dispose()

def test_authenticated_api(authenticated_api_context):
    """已认证 API 测试"""
    response = authenticated_api_context.get("http://localhost:8080/api/user")
    assert response.status == 200
    
    user_data = response.json()
    assert user_data["name"] == "Test User"
    assert user_data["email"] == "test@example.com"

API 响应验证

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# test_api_validation.py
@pytest.fixture
def api_validator():
    """API 响应验证器"""
    def validate_schema(response, schema):
        """验证响应模式"""
        data = response.json()
        
        def validate_object(obj, schema_def):
            for key, expected_type in schema_def.items():
                if key not in obj:
                    raise AssertionError(f"Missing key: {key}")
                
                if isinstance(expected_type, dict):
                    if not isinstance(obj[key], dict):
                        raise AssertionError(f"Expected dict for key: {key}")
                    validate_object(obj[key], expected_type)
                elif not isinstance(obj[key], expected_type):
                    raise AssertionError(f"Expected type {expected_type} for key: {key}, got {type(obj[key])}")
        
        validate_object(data, schema)
        return True
    
    return validate_schema

def test_api_schema_validation(authenticated_api_context, api_validator):
    """API 模式验证测试"""
    response = authenticated_api_context.get("http://localhost:8080/api/user")
    
    user_schema = {
        "id": int,
        "name": str,
        "email": str,
    }
    
    assert api_validator(response, user_schema)

组合应用

完整的测试环境

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# test_complete_environment.py
@pytest.fixture
def complete_test_environment(playwright):
    """完整的测试环境"""
    # 启动浏览器
    browser = playwright.chromium.launch(headless=True)
    
    # 创建上下文
    context = browser.new_context(
        viewport={"width": 1920, "height": 1080},
        locale="en-US",
        timezone_id="America/New_York",
        permissions=["geolocation", "notifications"]
    )
    
    # 创建页面
    page = context.new_page()
    
    # 创建 API 上下文
    api_context = playwright.request.new_context({
        "base_url": "http://localhost:8080",
        "extra_http_headers": {
            "Content-Type": "application/json"
        }
    })
    
    # 设置网络拦截
    def handle_route(route: Route):
        if "localhost:8080/api" in route.request.url:
            # 记录 API 调用
            print(f"API Call: {route.request.method} {route.request.url}")
        route.continue_()
    
    page.route("**/*", handle_route)
    
    # 设置性能监控
    performance_data = []
    
    def handle_metrics(metrics):
        performance_data.append(metrics)
    
    page.on("metrics", handle_metrics)
    
    # 创建测试工具集
    test_tools = {
        "page": page,
        "context": context,
        "browser": browser,
        "api_context": api_context,
        "performance_data": performance_data,
        
        # 工具方法
        "login": lambda username, password: (
            page.goto("http://localhost:8080"),
            page.fill("#username", username),
            page.fill("#password", password),
            page.click("#login-button"),
            page.wait_for_url("http://localhost:8080/dashboard")
        ),
        
        "screenshot_on_failure": lambda: page.screenshot(
            path=f"test-results/screenshots/failure_{int(time.time())}.png"
        ),
        
        "get_performance_metrics": lambda: performance_data[-1] if performance_data else None
    }
    
    yield test_tools
    
    # 清理
    page.close()
    context.close()
    browser.close()
    api_context.dispose()

def test_complete_workflow(complete_test_environment):
    """完整工作流测试"""
    tools = complete_test_environment
    
    # 创建登录表单
    tools["page"].set_content("""
    <!DOCTYPE html>
    <html>
    <head><title>Login</title></head>
    <body>
        <form id="login-form">
            <input type="text" id="username" placeholder="Username">
            <input type="password" id="password" placeholder="Password">
            <button type="submit" id="login-button">Login</button>
        </form>
        <script>
            document.getElementById('login-form').addEventListener('submit', async (e) => {
                e.preventDefault();
                const username = document.getElementById('username').value;
                const password = document.getElementById('password').value;
                
                const response = await fetch('/api/login', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({username, password})
                });
                
                if (response.ok) {
                    window.location.href = '/dashboard';
                }
            });
        </script>
    </body>
    </html>
    """)
    
    # 登录
    tools["login"]("testuser", "testpass")
    
    # 验证登录成功
    assert tools["page"].url == "http://localhost:8080/dashboard"
    
    # 验证用户信息显示
    user_info = tools["page"].locator("#user-info")
    assert user_info.is_visible()
    assert "Test User" in user_info.text_content()
    assert "test@example.com" in user_info.text_content()
    
    # 获取性能指标
    metrics = tools["get_performance_metrics"]()
    if metrics:
        print(f"页面性能指标: {metrics}")

测试数据管理

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
# test_data_management.py
import time

@pytest.fixture
def test_data_manager():
    """测试数据管理器"""
    class DataManager:
        def __init__(self):
            self.data = {}
        
        def create_user(self, **kwargs):
            """创建测试用户"""
            user_data = {
                "username": f"testuser_{int(time.time())}",
                "email": f"test_{int(time.time())}@example.com",
                "password": "TestPassword123!",
                **kwargs
            }
            self.data["user"] = user_data
            return user_data
        
        def create_product(self, **kwargs):
            """创建测试产品"""
            product_data = {
                "name": f"Test Product {int(time.time())}",
                "price": 99.99,
                "description": "Test product description",
                **kwargs
            }
            self.data["product"] = product_data
            return product_data
        
        def get_data(self, key):
            """获取测试数据"""
            return self.data.get(key)
        
        def cleanup(self):
            """清理测试数据"""
            self.data.clear()
    
    manager = DataManager()
    yield manager
    manager.cleanup()

@pytest.fixture
def data_driven_test_page(browser, test_data_manager):
    """数据驱动测试页面"""
    context = browser.new_context()
    page = context.new_page()
    
    # 创建测试数据
    test_user = test_data_manager.create_user()
    test_product = test_data_manager.create_product()
    
    # 注入测试数据到页面
    page._test_data = {
        "user": test_user,
        "product": test_product
    }
    
    yield page
    
    page.close()
    context.close()

def test_data_driven_workflow(data_driven_test_page):
    """数据驱动工作流测试"""
    page = data_driven_test_page
    test_data = page._test_data
    
    # 创建注册表单
    page.set_content(f"""
    <!DOCTYPE html>
    <html>
    <head><title>Register</title></head>
    <body>
        <form id="register-form">
            <input type="text" id="username" placeholder="Username">
            <input type="email" id="email" placeholder="Email">
            <input type="password" id="password" placeholder="Password">
            <button type="submit" id="register-button">Register</button>
        </form>
        <div id="welcome-message" style="display:none;"></div>
        <script>
            document.getElementById('register-form').addEventListener('submit', (e) => {{
                e.preventDefault();
                const username = document.getElementById('username').value;
                const email = document.getElementById('email').value;
                
                document.getElementById('welcome-message').style.display = 'block';
                document.getElementById('welcome-message').textContent = `Welcome ${{username}}!`;
            }});
        </script>
    </body>
    </html>
    """)
    
    # 使用测试数据进行测试
    page.goto("about:blank")
    page.fill("#username", test_data["user"]["username"])
    page.fill("#email", test_data["user"]["email"])
    page.fill("#password", test_data["user"]["password"])
    page.click("#register-button")
    
    # 验证注册成功
    welcome_message = page.locator("#welcome-message")
    assert welcome_message.is_visible()
    assert test_data["user"]["username"] in welcome_message.text_content()

性能优化

上下文复用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# test_context_reuse.py
@pytest.fixture(scope="session")
def shared_browser(playwright):
    """会话作用域的共享浏览器"""
    browser = playwright.chromium.launch(headless=True)
    yield browser
    browser.close()

@pytest.fixture(scope="module")
def shared_context(shared_browser):
    """模块作用域的共享上下文"""
    context = shared_browser.new_context()
    yield context
    context.close()

@pytest.fixture
def optimized_page(shared_context):
    """优化的页面 fixture"""
    page = shared_context.new_page()
    
    # 预加载常用资源
    page.goto("about:blank")
    page.evaluate("""
        () => {
            // 预加载常用库
            const preload = (src) => {
                const link = document.createElement('link');
                link.rel = 'preload';
                link.as = 'script';
                link.href = src;
                document.head.appendChild(link);
            };
            
            // 预加载常用脚本
            preload('http://localhost:8080/common.js');
        }
    """)
    
    yield page
    page.close()

def test_context_reuse(optimized_page):
    """测试上下文复用"""
    optimized_page.goto("http://localhost:8080")
    assert optimized_page.title() == "Test Page"

并行测试优化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# test_parallel_optimization.py
@pytest.fixture
def parallel_test_page(browser):
    """并行测试优化页面"""
    context = browser.new_context(
        # 减少资源消耗
        java_script_enabled=True,
        ignore_https_errors=True,
        bypass_csp=True
    )
    
    page = context.new_page()
    
    # 设置快速超时
    page.set_default_timeout(5000)
    page.set_default_navigation_timeout(10000)
    
    # 禁用不必要的功能
    def block_resources(route):
        if route.request.resource_type in ["image", "stylesheet", "font"]:
            route.abort()
        else:
            route.continue_()
    
    page.route("**/*", block_resources)
    
    yield page
    
    page.close()
    context.close()

@pytest.mark.parametrize("test_case", ["case1", "case2", "case3"])
def test_parallel_execution(parallel_test_page, test_case):
    """并行执行测试"""
    parallel_test_page.goto(f"http://localhost:8080?test={test_case}")
    
    # 快速验证
    assert parallel_test_page.locator("h1").is_visible()
    assert parallel_test_page.title() == "Test Page"

结语

以上就是关于Playwright python版本中关于Fixtures应用的一些进阶技巧总结。 公众号回复 playwright fixture 获取以上源码

9. 最佳实践总结

9.1 Fixture 设计原则

  1. 单一职责:每个 fixture 只负责一个功能
  2. 合理作用域:根据资源生命周期选择合适的作用域
  3. 资源清理:确保在 fixture 结束时正确清理资源
  4. 错误处理:在 fixture 中添加适当的错误处理

9.2 性能优化建议

  1. 复用资源:使用会话或模块作用域复用浏览器和上下文
  2. 并行执行:设计支持并行执行的 fixtures
  3. 资源管理:合理管理内存和网络资源
  4. 超时设置:根据测试需求设置合理的超时时间

9.3 维护性建议

  1. 文档化:为复杂的 fixtures 添加文档字符串
  2. 类型提示:使用类型提示提高代码可读性
  3. 配置化:将硬编码值提取为配置参数
  4. 模块化:将相关功能组织到模块中

通过这些完全可执行的示例,您可以深入理解 Playwright Python fixtures 的高级应用技巧,并在实际项目中应用这些模式来构建高效、可靠的测试框架。

使用 Hugo 构建
主题 StackJimmy 设计