Mocking 是 route 方法最重要的应用之一,用于在测试中模拟后端 API 响应,实现测试与真实后端服务的解耦。
1. 基础 Mocking 模式
基本响应模拟
python
from playwright.sync_api import sync_playwright
def test_basic_mock():
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
# 模拟用户列表 API
def mock_users(route):
route.fulfill(
status=200,
content_type="application/json",
body='{"users": [{"id": 1, "name": "张三"}, {"id": 2, "name": "李四"}]}'
)
page.route("**/api/users", mock_users)
page.goto("http://localhost:3000")
# 此时页面调用 /api/users 将返回模拟数据
browser.close()
2. 动态 Mocking 数据
基于请求参数的动态响应
python
def dynamic_mock_based_on_request(route):
request = route.request
# 根据查询参数返回不同数据
if "user_id=1" in request.url:
route.fulfill(
status=200,
body='{"id": 1, "name": "管理员", "role": "admin"}'
)
elif "user_id=2" in request.url:
route.fulfill(
status=200,
body='{"id": 2, "name": "普通用户", "role": "user"}'
)
else:
route.fulfill(status=404, body='{"error": "用户不存在"}')
page.route("**/api/user**", dynamic_mock_based_on_request)
基于请求体的动态响应
python
def mock_based_on_post_data(route):
request = route.request
if request.method == "POST" and request.post_data:
post_data = request.post_data
if "login" in request.url:
# 解析登录数据
if "admin" in post_data:
route.fulfill(
status=200,
body='{"token": "admin-token", "user": {"role": "admin"}}'
)
else:
route.fulfill(
status=200,
body='{"token": "user-token", "user": {"role": "user"}}'
)
else:
route.continue_()
else:
route.continue_()
page.route("**/api/**", mock_based_on_post_data)
3. 复杂业务场景 Mocking
完整的 CRUD 操作模拟
python
class UserServiceMock:
def __init__(self):
self.users = [
{"id": 1, "name": "张三", "email": "zhang@example.com"},
{"id": 2, "name": "李四", "email": "li@example.com"}
]
self.next_id = 3
def handle_user_routes(self, route):
request = route.request
if request.method == "GET":
self.handle_get(route)
elif request.method == "POST":
self.handle_post(route)
elif request.method == "PUT":
self.handle_put(route)
elif request.method == "DELETE":
self.handle_delete(route)
def handle_get(self, route):
if route.request.url.endswith("/api/users"):
# 获取用户列表
route.fulfill(
status=200,
body=json.dumps({"users": self.users})
)
else:
# 获取单个用户
user_id = int(route.request.url.split("/")[-1])
user = next((u for u in self.users if u["id"] == user_id), None)
if user:
route.fulfill(status=200, body=json.dumps(user))
else:
route.fulfill(status=404, body='{"error": "用户不存在"}')
def handle_post(self, route):
# 创建用户
post_data = json.loads(route.request.post_data)
new_user = {
"id": self.next_id,
"name": post_data["name"],
"email": post_data["email"]
}
self.users.append(new_user)
self.next_id += 1
route.fulfill(status=201, body=json.dumps(new_user))
def handle_put(self, route):
# 更新用户
user_id = int(route.request.url.split("/")[-1])
user_data = json.loads(route.request.post_data)
for user in self.users:
if user["id"] == user_id:
user.update(user_data)
route.fulfill(status=200, body=json.dumps(user))
return
route.fulfill(status=404, body='{"error": "用户不存在"}')
def handle_delete(self, route):
# 删除用户
user_id = int(route.request.url.split("/")[-1])
self.users = [u for u in self.users if u["id"] != user_id]
route.fulfill(status=204)
# 使用模拟服务
user_mock = UserServiceMock()
page.route("**/api/users**", user_mock.handle_user_routes)
4. 错误场景模拟
HTTP 错误状态模拟
python
def mock_error_scenarios(route):
# 模拟各种错误场景
if "/api/timeout" in route.request.url:
# 模拟超时(需要异步处理)
route.fulfill(status=408, body='{"error": "请求超时"}')
elif "/api/server-error" in route.request.url:
route.fulfill(status=500, body='{"error": "服务器内部错误"}')
elif "/api/not-found" in route.request.url:
route.fulfill(status=404, body='{"error": "资源不存在"}')
elif "/api/unauthorized" in route.request.url:
route.fulfill(status=401, body='{"error": "未授权"}')
elif "/api/forbidden" in route.request.url:
route.fulfill(status=403, body='{"error": "禁止访问"}')
else:
route.continue_()
page.route("**/api/**", mock_error_scenarios)
网络异常模拟
python
def mock_network_issues(route):
import random
scenario = random.choice(["timeout", "error", "success"])
if scenario == "timeout":
# 在实际项目中可能需要异步处理
route.fulfill(status=408, body='{"error": "请求超时"}')
elif scenario == "error":
route.fulfill(status=500, body='{"error": "服务器错误"}')
else:
route.fulfill(
status=200,
body='{"status": "success", "data": "正常响应"}'
)
# 模拟不稳定的网络
page.route("**/api/unstable**", mock_network_issues)
5. 高级 Mocking 技巧
基于请求头的条件 Mocking
python
def mock_based_on_headers(route):
request = route.request
auth_header = request.headers.get("authorization", "")
if "admin-token" in auth_header:
# 管理员权限数据
route.fulfill(
status=200,
body='{"data": "管理员数据", "permissions": ["read", "write", "delete"]}'
)
elif "user-token" in auth_header:
# 普通用户数据
route.fulfill(
status=200,
body='{"data": "用户数据", "permissions": ["read"]}'
)
else:
# 未授权
route.fulfill(status=401, body='{"error": "未授权"}')
page.route("**/api/secure-data**", mock_based_on_headers)
延迟响应模拟
python
import asyncio
async def mock_with_delay(route):
# 模拟网络延迟
await asyncio.sleep(2)
await route.fulfill(
status=200,
body='{"message": "延迟响应", "delay": 2000}'
)
await page.route("**/api/slow**", mock_with_delay)
6. 测试用例中的最佳实践
使用 fixture 封装 Mocking
python
import pytest
from playwright.sync_api import Page
@pytest.fixture
def setup_user_mock(page: Page):
"""设置用户相关的 API Mock"""
def mock_users(route):
route.fulfill(
status=200,
body=json.dumps({
"users": [
{"id": 1, "name": "测试用户1", "active": True},
{"id": 2, "name": "测试用户2", "active": False}
]
})
)
page.route("**/api/users", mock_users)
return page
def test_user_list(setup_user_mock):
page = setup_user_mock
page.goto("/users")
# 断言页面显示了模拟的用户数据
assert page.text_content(".user-name") == "测试用户1"
动态修改 Mock 数据
python
class MockManager:
def __init__(self, page):
self.page = page
self.user_data = {"users": []}
def setup_user_mock(self):
def user_handler(route):
route.fulfill(
status=200,
body=json.dumps(self.user_data)
)
self.page.route("**/api/users", user_handler)
def set_user_data(self, users):
self.user_data = {"users": users}
# 在测试中使用
def test_different_user_scenarios():
mock_manager = MockManager(page)
mock_manager.setup_user_mock()
# 测试空用户列表
mock_manager.set_user_data([])
page.goto("/users")
assert page.locator(".no-users").is_visible()
# 测试有用户的情况
mock_manager.set_user_data([{"id": 1, "name": "测试用户"}])
page.reload()
assert page.locator(".user-item").is_visible()
7. 调试和验证
验证 Mock 是否生效
python
def mock_with_verification(route):
print(f"Mocking request: {route.request.url}")
print(f"Request method: {route.request.method}")
print(f"Request headers: {route.request.headers}")
# 记录 Mock 被调用的次数
mock_with_verification.call_count = getattr(mock_with_verification, 'call_count', 0) + 1
route.fulfill(
status=200,
body='{"status": "mocked"}'
)
mock_with_verification.call_count = 0
page.route("**/api/test**", mock_with_verification)
# 测试完成后验证
assert mock_with_verification.call_count > 0, "Mock 应该被调用"
这些 Mocking 技术可以帮助你创建稳定、可靠的测试环境,确保测试不依赖外部服务,提高测试速度和可靠性。