Playwright Route类实战:从拦截到篡改,构建灵活测试场景
1. Playwright Route类入门拦截请求的基本原理第一次接触Playwright的Route类时我完全被它的能力震惊了。想象一下你正在测试一个电商网站突然想看看当支付接口返回500错误时前端页面会如何展示。传统做法可能需要搭建mock服务器或者修改后端代码但有了Route类只需要几行代码就能实现这个场景。Route类的核心原理就像高速公路上的检查站。当浏览器发出网络请求时Playwright会在请求真正发送前将其拦截下来交给我们自定义的处理函数。这个处理函数可以决定是放行continue、拒绝abort还是伪造响应fulfill。这种机制让我们能够在不修改实际服务端代码的情况下模拟各种网络环境。这里有个最简单的例子我们拦截所有图片请求并阻止加载from playwright.sync_api import sync_playwright def handle_route(route): if route.request.resource_type image: route.abort() else: route.continue_() with sync_playwright() as p: browser p.chromium.launch() page browser.new_page() page.route(**/*, handle_route) page.goto(https://example.com)这个脚本会阻止页面加载所有图片资源对于测试页面在禁用图片时的布局非常有用。route.request.resource_type可以判断请求类型常见值还有stylesheet、script、xhr等。2. 拦截实战四种核心方法详解2.1 abort模拟请求失败场景在实际项目中我最常用abort()来测试前端错误处理。比如测试一个文件上传功能时可以这样模拟上传中断def handle_upload(route): if /api/upload in route.request.url: print(模拟上传中断) route.abort(error_codefailed) # 可选错误码timeout, failed, aborted等 else: route.continue_() page.route(**/*, handle_upload)error_code参数特别有用不同错误码会触发不同的网络错误事件。比如timeout会模拟请求超时accessdenied会模拟CORS错误。我曾经用这个功能发现了前端没有正确处理403错误的bug。2.2 continue修改请求头实战continue()不只是简单放行请求还能先修改请求内容。比如测试CSRF防护时可以这样移除tokendef remove_csrf_token(route): headers route.request.headers del headers[X-CSRF-Token] route.continue_(headersheaders) page.route(**/api/*, remove_csrf_token)这个技巧帮我发现了后端没有正确验证CSRF token的安全漏洞。continue()还可以修改postData来篡改请求体这在测试表单提交时特别有用。2.3 fulfill完全掌控响应内容fulfill()是我最喜欢的特性它可以完全自定义响应。比如模拟一个分页接口def mock_pagination(route): if /api/users in route.request.url: page int(route.request.url.split(page)[1]) mock_data { data: [{id: i, name: fUser {i}} for i in range(page*10, (page1)*10)], total: 100 } route.fulfill( status200, headers{Content-Type: application/json}, bodyjson.dumps(mock_data) ) else: route.continue_()这个mock数据会随着page参数变化可以完美测试前端分页逻辑。我经常用这个方法来测试边界情况比如当total为0时的空状态展示。2.4 fetch fulfill修改真实响应有时候我们需要基于真实响应进行修改这时可以组合使用fetch()和fulfill()async def modify_response(route): response await route.fetch() json_data await response.json() json_data[price] * 0.9 # 打九折 await route.fulfill(responseresponse, jsonjson_data) page.route(**/product/*, modify_response)这个例子会先获取真实响应然后修改价格字段后再返回。我在测试促销活动页面时经常用这招比直接mock更接近真实场景。3. 复杂场景实战技巧3.1 模拟限流和降级测试API限流时我们可以随机拒绝请求import random def rate_limit(route): if random.random() 0.3: # 30%概率限流 route.fulfill( status429, body{error: Too many requests} ) else: route.continue_() page.route(**/api/*, rate_limit)对于服务降级测试可以这样返回简化版数据def degrade_service(route): if /api/complex in route.request.url: route.fulfill( status200, body{basic: true, message: Service degraded} ) else: route.continue_()3.2 动态路由匹配Route支持多种匹配方式非常灵活# 正则匹配 page.route(re.compile(r\.(jpg|png)$), lambda route: route.abort()) # 函数匹配 def should_intercept(request): return analytics in request.url and request.method POST page.route(should_intercept, track_analytics)我曾经用动态匹配实现了只在特定时间段拦截请求的功能用来测试时间敏感型功能。3.3 请求/响应钩子除了修改内容Route还可以用来收集数据api_calls [] def collect_metrics(route): start_time time.time() route.continue_() api_calls.append({ url: route.request.url, duration: time.time() - start_time }) page.route(**/api/**, collect_metrics)这个技巧帮我找出了几个性能瓶颈API。同样的原理也可以用来做自动化监控。4. 最佳实践与常见陷阱4.1 性能优化建议Route拦截会带来性能开销我有几个优化心得尽量缩小拦截范围避免使用**/*这样的宽泛匹配在不需要时及时取消拦截page.unroute(**/api/**)对于复杂逻辑使用async/await避免阻塞4.2 调试技巧调试Route时我常用的方法打印request和response详情print(f{request.method} {request.url}) print(fHeaders: {request.headers}) print(fPost Data: {request.post_data})使用page.pause()在拦截时暂停配合Playwright的trace功能记录完整过程4.3 常见问题解决我踩过的一些坑忘记调用continue()或fulfill()导致请求挂起修改了不可变的header字段如Content-Length异步上下文问题特别是在Pytest中使用时正则表达式性能问题导致超时Route类是Playwright最强大的功能之一但需要合理使用。我建议从简单场景开始逐步尝试更复杂的拦截逻辑。在实际项目中这些技巧帮我节省了数百小时的测试时间特别是对于复杂的前端状态测试和边缘场景验证。

相关新闻