# PicoClaw 测试策略
## 测试金字塔
```{mermaid}
graph TB
E2E["E2E 测试
(手动 + CI)"]
INT["集成测试
(*_integration_test.go)"]
UNIT["单元测试
(*_test.go × 162)"]
style E2E fill:#f96,stroke:#333
style INT fill:#ff9,stroke:#333
style UNIT fill:#9f9,stroke:#333
E2E --> INT --> UNIT
```
| 层次 | 数量 | 范围 | 运行方式 |
|------|------|------|----------|
| 单元测试 | 162 文件 | 函数/方法级别 | `make test` |
| 集成测试 | ~5 文件 | Provider API 调用 | 需要 API Key |
| E2E 测试 | 手动 | 完整消息流程 | Docker Compose |
## 单元测试分布
| 包 | 测试文件数 | 关键测试 |
|----|-----------|----------|
| `pkg/agent/` | 7 | context_cache, loop, thinking, registry |
| `pkg/auth/` | 5 | oauth, pkce, token, store, anthropic_usage |
| `pkg/bus/` | 1 | bus 消息发布/订阅 |
| `pkg/channels/` | 6 | base, manager, split, errors, errutil, command |
| `pkg/config/` | 3 | config, migration, model_config, version |
| `pkg/credential/` | 3 | credential, keygen, store |
| `pkg/cron/` | 1 | service |
| `pkg/heartbeat/` | 1 | service |
| `pkg/identity/` | 1 | identity |
| `pkg/mcp/` | 1 | manager |
| `pkg/memory/` | 2 | jsonl, migration |
| `pkg/providers/` | ~15 | factory, fallback, cooldown, error_classifier, 各 provider |
| `pkg/routing/` | 4 | agent_id, route, router, session_key |
| `pkg/session/` | 2 | jsonl_backend, manager |
| `pkg/skills/` | 5 | clawhub_registry, installer, loader, registry, search_cache |
| `pkg/state/` | 1 | state |
| `pkg/tools/` | ~15 | shell, edit, filesystem, web, cron, spawn, message, mcp_tool 等 |
| `pkg/voice/` | 1 | transcriber |
| `cmd/picoclaw/` | ~20 | 各子命令的单元测试 |
## 测试运行
```bash
# 运行所有单元测试
make test
# 运行特定包的测试
go test ./pkg/agent/...
go test ./pkg/providers/...
# 运行特定测试
go test ./pkg/agent/ -run TestAgentLoop
# 带覆盖率
go test -cover ./pkg/...
# 生成覆盖率报告
go test -coverprofile=coverage.out ./pkg/...
go tool cover -html=coverage.out
# 竞态检测
go test -race ./pkg/...
```
## 集成测试
集成测试文件以 `_integration_test.go` 结尾,需要真实 API Key:
```bash
# Claude CLI Provider 集成测试
ANTHROPIC_API_KEY=sk-ant-xxx go test ./pkg/providers/ -run Integration -v
# Codex CLI Provider 集成测试
OPENAI_API_KEY=sk-xxx go test ./pkg/providers/ -run Integration -v
```
## Mock 策略
- **Provider Mock**:`pkg/agent/mock_provider_test.go` 提供 `MockProvider` 实现
- **接口驱动**:所有外部依赖通过接口抽象,便于 Mock
- **无外部 Mock 框架**:使用手写 Mock(Go 标准实践)
```go
// mock_provider_test.go
type MockProvider struct {
ChatFunc func(ctx context.Context, messages []Message, ...) (*LLMResponse, error)
}
func (m *MockProvider) Chat(ctx context.Context, messages []Message, ...) (*LLMResponse, error) {
return m.ChatFunc(ctx, messages, ...)
}
```
## CI/CD 测试
### PR 检查(`.github/workflows/pr.yml`)
```yaml
jobs:
lint: # golangci-lint
test: # go test ./...
build: # make build-all
```
### 主分支构建(`.github/workflows/build.yml`)
```yaml
jobs:
build: # make build-all(所有平台交叉编译)
```
### Nightly 构建(`.github/workflows/nightly.yml`)
- 每日 UTC 0:00 自动构建
- 发布 nightly 预发布版本
## 关键测试场景
### Agent 循环
- 正常消息处理(无工具调用)
- 单次工具调用
- 多次工具调用迭代
- 达到最大迭代限制
- 上下文摘要触发
- 子 Agent spawn 和完成
### Provider 故障转移
- 主 Provider 认证失败 → 切换备选
- 主 Provider 限流 → 切换备选 + 冷却
- 所有 Provider 失败 → 返回错误
- Format 错误 → 不重试
### 渠道消息
- 长消息自动分片
- 媒体附件处理
- 群组消息过滤(mention_only)
- allow_from 白名单
### 工具安全
- 文件系统沙箱(restrict_to_workspace)
- 路径白名单验证
- Shell 命令危险操作拦截
- 符号链接路径规范化