测试策略
运行测试
必须使用 run_tests.sh
bash
# 完整测试套件
scripts/run_tests.sh
# 单个目录
scripts/run_tests.sh tests/gateway/
# 单个测试
scripts/run_tests.sh tests/agent/test_foo.py::test_x
# 传递 pytest 参数
scripts/run_tests.sh -v --tb=long永远不要直接调用 pytest。scripts/run_tests.sh 确保本地环境与 CI 一致:
| 环境变量 | 不使用脚本 | 使用脚本 |
|---|---|---|
| Provider API Keys | 你的真实密钥 | 全部清空 |
| HOME / ~/.hermes/ | 你的真实配置 | 临时目录 |
| 时区 | 本地时区 | UTC |
| 语言 | 本地语言 | C.UTF-8 |
| xdist workers | -n auto (20+核) | -n 4 (匹配 CI) |
如果必须直接运行 pytest
bash
source venv/bin/activate
python -m pytest tests/ -q -n 4测试架构
conftest.py 自动隔离
tests/conftest.py 提供了 autouse fixture:
- 重定向
HERMES_HOME到临时目录 - 清空所有 API key 环境变量
- 设置
TZ=UTC、LANG=C.UTF-8 - 每个 test function 获得独立的临时目录
测试目录结构
tests/
├── conftest.py # autouse 隔离 fixture
├── agent/ # agent/ 模块测试
├── cli/ # CLI 测试
├── gateway/ # 网关测试
├── tools/ # 工具测试
├── hermes_cli/ # CLI 子命令测试
├── cron/ # 定时任务测试
├── skills/ # 技能系统测试
├── run_agent/ # Agent 运行测试
├── tui_gateway/ # TUI 后端测试
├── integration/ # 集成测试(需要外部服务)
├── e2e/ # 端到端测试
├── fakes/ # 测试替身
└── environments/ # 环境后端测试测试标记
@pytest.mark.integration— 需要外部服务的测试,默认跳过- 无标记 — 单元测试,默认运行
不要写 Change-Detector 测试
什么是 change-detector 测试
断言预期会变化的数据的测试。这类测试没有行为覆盖价值,只会在数据更新时破坏 CI。
不要写
python
# 快照测试 — 每次模型更新都会失败
assert "gemini-2.5-pro" in _PROVIDER_MODELS["gemini"]
# 版本号断言 — 每次配置升级都会失败
assert DEFAULT_CONFIG["_config_version"] == 21
# 计数断言 — 每次添加 provider/skill 都会失败
assert len(_PROVIDER_MODELS["huggingface"]) == 8应该写
python
# 行为测试:验证机制是否工作
assert "gemini" in _PROVIDER_MODELS
assert len(_PROVIDER_MODELS["gemini"]) >= 1
# 不变量测试:验证数据间的关系
for m in _PROVIDER_MODELS["huggingface"]:
assert m.lower() in DEFAULT_CONTEXT_LENGTHS_LOWER
# 迁移测试:验证升级到最新版本
assert raw["_config_version"] == DEFAULT_CONFIG["_config_version"]判断规则
- 如果测试看起来像当前数据的快照 → 删除
- 如果测试断言两个数据之间的关系 → 保留
Profile 测试模式
测试 Profile 功能时,需要同时 mock Path.home() 和设置 HERMES_HOME:
python
@pytest.fixture
def profile_env(tmp_path, monkeypatch):
home = tmp_path / ".hermes"
home.mkdir()
monkeypatch.setattr(Path, "home", lambda: tmp_path)
monkeypatch.setenv("HERMES_HOME", str(home))
return home