Selenium自动化测试中mitmproxy的高级流量操控实战
当你在Selenium自动化测试中遇到需要模拟特定网络条件、修改API响应或绕过前端限制的场景时,mitmproxy这个基于Python的中间人代理工具能成为你的秘密武器。不同于Fiddler或Charles这类图形化工具,mitmproxy提供了完整的Python API,允许你在测试流程中动态操控每一个网络请求。
1. 为什么选择mitmproxy进行Selenium测试?
在自动化测试领域,mitmproxy提供了三大不可替代的优势:
- 实时流量操控:可以在测试运行时动态修改请求和响应,无需停止测试流程
- 深度集成能力:作为Python工具,可直接与pytest/unittest等测试框架共享上下文
- 无头环境支持:完美适配CI/CD环境中的无头浏览器测试场景
对比常见抓包工具:
| 工具特性 | Fiddler/Charles | mitmproxy |
|---|---|---|
| 脚本化控制 | 有限 | 完全支持 |
| 无头环境兼容性 | 较差 | 优秀 |
| 定制化程度 | 中等 | 极高 |
| 性能开销 | 较高 | 较低 |
提示:当测试需要模拟慢速网络时,mitmproxy可以通过延迟响应实现更真实的测试场景
2. 核心配置:让Selenium流量经过mitmproxy
正确配置代理是使用mitmproxy的第一步,以下是三种不同精度的配置方案:
2.1 全局代理配置(适合调试)
from selenium import webdriver options = webdriver.ChromeOptions() options.add_argument("--proxy-server=127.0.0.1:8080") driver = webdriver.Chrome(options=options)这种配置简单直接,但会影响所有流量,包括测试框架自身的API调用。
2.2 精细化代理控制(推荐方案)
from selenium.webdriver.common.proxy import Proxy, ProxyType proxy = Proxy({ 'proxyType': ProxyType.MANUAL, 'httpProxy': '127.0.0.1:8080', 'sslProxy': '127.0.0.1:8080', 'noProxy': 'localhost,127.0.0.1' # 排除内部请求 }) capabilities = webdriver.DesiredCapabilities.CHROME proxy.add_to_capabilities(capabilities) driver = webdriver.Chrome(desired_capabilities=capabilities)2.3 多代理环境配置
当需要同时监控多个浏览器实例时:
from selenium import webdriver from selenium.webdriver.common.proxy import Proxy def create_driver_with_proxy(port): proxy = Proxy({ 'proxyType': 'MANUAL', 'httpProxy': f'127.0.0.1:{port}', 'sslProxy': f'127.0.0.1:{port}' }) capabilities = webdriver.DesiredCapabilities.CHROME proxy.add_to_capabilities(capabilities) return webdriver.Chrome(desired_capabilities=capabilities) # 启动两个使用不同代理端口的浏览器 driver1 = create_driver_with_proxy(8080) # mitmproxy实例1 driver2 = create_driver_with_proxy(8081) # mitmproxy实例23. 解决HTTPS流量捕获的证书问题
即使配置了代理,HTTPS网站仍可能出现安全警告。以下是系统化的解决方案:
3.1 安装mitmproxy根证书
# 生成证书存放目录 mkdir -p ~/.mitmproxy # 启动mitmproxy自动生成证书 mitmdump & killall mitmdump # 生成后立即停止 # 将证书安装到系统信任库 certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "mitmproxy" -i ~/.mitmproxy/mitmproxy-ca-cert.pem3.2 Selenium自动信任证书
对于ChromeDriver,可通过以下参数自动接受不安全证书:
options = webdriver.ChromeOptions() options.add_argument('--ignore-certificate-errors') options.add_argument('--allow-running-insecure-content') options.add_argument('--disable-web-security') driver = webdriver.Chrome(options=options)3.3 针对Firefox的特殊配置
profile = webdriver.FirefoxProfile() profile.accept_untrusted_certs = True profile.assume_untrusted_cert_issuer = False driver = webdriver.Firefox(firefox_profile=profile)4. 实战:动态修改API响应
mitmproxy的真正威力在于其脚本化控制能力。创建一个addon.py文件:
from mitmproxy import http def response(flow: http.HTTPFlow) -> None: # 修改特定API的响应 if "api/v1/user" in flow.request.url: original_json = flow.response.json() original_json["username"] = "test_user" flow.response.text = str(original_json) # 注入测试标记 if flow.response.headers.get("content-type", "").startswith("text/html"): html = flow.response.text html = html.replace("</body>", "<script>window.__TEST_MODE__=true;</script></body>") flow.response.text = html启动mitmproxy加载脚本:
mitmweb -s addon.py常见响应修改场景:
- Mock数据:替换API返回的测试数据
- 错误注入:模拟服务器错误响应
- 功能开关:修改前端配置标记
- 性能测试:延迟特定资源加载
5. 高级技巧与疑难解决
5.1 处理WebSocket流量
mitmproxy同样支持WebSocket流量监控和修改:
def websocket_message(flow: http.HTTPFlow): if flow.websocket and "chat" in flow.request.url: last_message = flow.websocket.messages[-1] if b"password" in last_message.content: last_message.content = last_message.content.replace( b"password=123456", b"password=******" )5.2 性能优化配置
当处理大量请求时,调整以下参数:
class MyAddon: def load(self, loader): loader.add_option( name="stream_large_bodies", typespec=str, default="1m", help="Stream data larger than this size" ) def running(self): print(f"当前性能配置:{ctx.options.stream_large_bodies}")5.3 常见问题排查
流量未经过代理:
- 检查浏览器是否配置了其他代理扩展
- 验证
chrome://net-internals/#proxy的实际代理设置
HTTPS网站无法加载:
- 确认证书已正确安装
- 检查是否开启了
--ignore-certificate-errors
修改响应未生效:
- 确保脚本没有语法错误
- 检查URL匹配逻辑是否准确
6. 测试场景实战案例
6.1 A/B测试验证
def response(flow): if flow.request.url.endswith("/experiment/variant"): flow.response.text = '{"variant": "B"}'6.2 地理围栏测试
def response(flow): if "geo-api" in flow.request.url: flow.response.text = '{"country": "US", "region": "CA"}'6.3 性能基准测试
import time def request(flow): if "critical-api" in flow.request.url: flow.request.headers["X-Start-Time"] = str(time.time()) def response(flow): if "critical-api" in flow.request.url: start = float(flow.request.headers["X-Start-Time"]) latency = time.time() - start print(f"API响应延迟:{latency:.2f}s")在持续集成环境中,建议将mitmproxy作为fixture管理:
import pytest from mitmproxy.tools.main import mitmdump @pytest.fixture(scope="session") def proxy_server(): process = mitmdump(["-s", "addon.py", "--listen-port", "8080"]) yield process.terminate()这种深度集成方式让网络操控成为自动化测试的自然组成部分,而非外部依赖。当测试需要验证极端网络条件时,mitmproxy提供的--set connection_strategy=lazy等参数可以模拟各种网络异常情况。