测试策略
学习目标
理解 CrewAI 如何通过 VCR 录制、pytest 插件生态和跨提供商测试策略保证代码质量,同时避免测试时调用真实 LLM API。
项目实践
VCR 录制 HTTP 交互
CrewAI 使用 vcrpy + pytest-recording 录制和回放 HTTP 交互:
lib/crewai/tests/cassettes/目录存放录制的 HTTP 交互- 测试运行时不再调用真实 LLM API
- 使用
pytest.mark.vcr装饰器标记需要录制的测试
关键优势:避免测试时调用真实 LLM API,节省成本和时间。
pytest 插件生态
CrewAI 使用了丰富的 pytest 插件:
| 插件 | 用途 |
|---|---|
pytest-asyncio | 异步测试支持 |
pytest-subprocess | 子进程模拟 |
pytest-recording | VCR HTTP 录制 |
pytest-randomly | 随机化测试顺序,防止顺序依赖 |
pytest-timeout | 测试超时保护 |
pytest-xdist | 多进程并行测试 |
pytest-split | 将大测试套件分片 |
vcrpy | HTTP 交互录制(固定版本,<7.0 会破坏测试) |
测试目录结构
测试按子系统组织:
lib/crewai/tests/├── agents/ # Agent 测试├── crew/ # Crew 编排测试├── events/ # 事件系统测试├── knowledge/ # 知识系统测试├── llms/ # LLM 提供商测试├── mcp/ # MCP 集成测试├── memory/ # 记忆系统测试├── rag/ # RAG 测试├── skills/ # Skills 测试├── storage/ # 存储测试├── tools/ # 工具测试└── fixtures/ # 共享测试夹具跨提供商测试策略
LLM 提供商测试需要验证 OpenAI/Anthropic/Gemini 等不同提供商的行为一致性:
- 使用
pytest.mark.parametrize对同一测试逻辑运行多个提供商 - VCR cassette 为每个提供商录制独立的 HTTP 响应
- 测试断言关注输出格式的一致性,而非响应内容
问题与规避
VCR cassette 过期
- API 响应格式变化导致 cassette 回放失败
- 对策:定期重新录制 cassette(如每季度一次),或在 API 版本升级时重新录制
pytest-randomly 导致测试不稳定
- 随机化测试顺序可能暴露隐藏的顺序依赖
- 对策:使用
--randomly-seed固定种子复现失败,修复后移除种子
pytest-split 分片不均
- 某些测试文件远大于其他文件,导致 CI 分片时间不均
- 对策:使用
--splits+--group按执行时间而非文件数分片
vcrpy 版本锁定
- CrewAI 将
vcrpy固定在~=25.1.0,其他版本会破坏pytest-recording - 对策:不要手动升级
vcrpy版本
设计取舍
VCR 录制 vs Mock
- VCR 录制的优势是真实反映 API 行为,mock 可能遗漏 API 格式变化
- 代价是 cassette 文件随 API 数量增长,需要维护
- CrewAI 选择 VCR 录制,因为它测试的不仅是代码逻辑,还有与 API 的集成契约
pytest-xdist vs 顺序执行
- xdist 将测试分片到多个进程并行执行,缩短 CI 时间
- 代价是测试之间无法共享状态(如全局单例的
crewai_event_bus) - CrewAI 的测试设计为无状态(每个测试创建独立的 Crew/Agent),适合 xdist
参考来源
- 源码验证:
pyproject.toml— dev dependencies - 源码验证:
lib/crewai/tests/目录结构 - 源码验证:
lib/crewai/tests/cassettes/— VCR 录制的 HTTP 交互