测试策略与 CI 实践
学习目标
- 理解 Browser Use 的测试哲学:“禁止 mock,使用真实对象”
- 掌握 pytest-httpserver 在浏览器测试中的应用
- 了解 CI 流程和测试门禁
项目实践
测试哲学
Browser Use 有一条明确的测试规则(写入 CLAUDE.md):
Never mock anything in tests, always use real objects!! The only exception is the LLM.
为什么:
- 真实对象测试确保集成正确性
- Mock 容易遗漏真实环境中的边界情况
- LLM 是唯一例外:调用真实 LLM 成本高、不稳定、不可控
LLM Mock 模式
通过 pytest fixtures 设置 LLM 响应:
# conftest.py 中的 fixture@pytest.fixturedef mock_llm(): """返回模拟的 LLM,预设响应""" return MockLLM(responses=[...])
# 测试中使用async def test_agent_step(mock_llm): agent = Agent(task="...", llm=mock_llm) await agent.run()pytest-httpserver 模拟网页
禁止使用真实远程 URL(如 https://google.com),必须用 pytest-httpserver 模拟:
@pytest.fixturedef test_server(httpserver): """设置本地测试服务器""" html = """ <html> <body> <a href="/page2">Go to page 2</a> <button id="submit">Submit</button> </body> </html> """ httpserver.expect_request("/").respond_with_data(html, content_type="text/html") return httpserver
async def test_click_element(test_server): url = test_server.url_for("/") browser = BrowserSession() await browser.navigate(url) # ... 测试点击每个测试独立设置 HTML:
- 为每个测试场景创建特定的 HTML 页面
- 精确控制页面内容,避免外部依赖
- 测试可重复、可离线运行
测试分类
通过 pytest markers 分类:
| Marker | 说明 | 运行方式 |
|---|---|---|
slow | 慢速测试(真实浏览器交互) | -m 'not slow' 跳过 |
integration | 集成测试 | 需要完整环境 |
unit | 单元测试 | 快速运行 |
asyncio | 异步测试 | 自动处理 event loop |
CI 测试门禁
CI 测试路径:tests/ci/
- 所有通过的测试移入此目录
- CI 自动发现并运行此目录下的所有测试
- 特定场景的测试放在对应文件(如
tests/ci/test_action_ClickElement.py)
运行命令:
# CI 测试(快速子集)uv run pytest -vxs tests/ci
# 所有测试uv run pytest -vxs tests/
# 单个测试文件uv run pytest -vxs tests/ci/test_specific_test.pypytest 配置(pyproject.toml):
[tool.pytest.ini_options]timeout = 300asyncio_mode = "auto"asyncio_default_fixture_loop_scope = "session"addopts = "-svx --strict-markers --tb=short --dist=loadscope"并行测试:使用 pytest-xdist 的 --dist=loadscope 模式,按测试文件并行而非按测试函数。
质量门禁
测试通过后移入 CI
开发流程:
- 新功能/修复在任意位置编写测试
- 测试通过后移入
tests/ci/ - 去重和压缩:将重复的测试逻辑合并到一个文件
- CI 自动运行
问题与规避
浏览器测试不稳定
问题:浏览器启动、渲染可能不稳定,导致 flaky tests。
对策:
- 300s 超时保护
- 使用
pytest-httpserver消除网络不稳定 - LLM 使用 mock 消除模型不稳定
- 特定场景用
pytest.mark.flaky标记(如果有的话)
测试耗时过长
问题:浏览器测试每个操作都需要启动浏览器,耗时高。
对策:
- CI 只运行
tests/ci/快速子集 - 慢速测试用
@pytest.mark.slow标记,可选择性跳过 pytest-xdist并行运行
设计取舍
全真实对象 vs 部分 Mock
| 方案 | 优势 | 代价 |
|---|---|---|
| 全真实对象(当前) | 集成正确性好 | 测试耗时、依赖环境 |
| 部分 Mock | 测试快速 | 可能遗漏集成问题 |
Browser Use 选择全真实对象(除 LLM 外),因为:
- 浏览器交互的核心价值就是”端到端能工作”
- 单元测试无法发现 CDP 协议变化、浏览器行为变化等问题
- 代价是测试更慢,但 CI 通过
tests/ci/子集和并行来缓解