agentleFS
Sign inSign up

vetta-testing

openvetta/open-vetta/.agents/skills/vetta-testing/SKILL.md

为 OpenVetta 的功能变更、Bug 修复、重构、公共合同和 UI 交互设计、编写或审查测试;在决定测试范围、补充回归测试、覆盖用户常见使用流程或评估测试质量时使用。纯文档或没有运行时行为变化的编辑不使用。

Skill277 starsChanged 9 days ago
---
name: vetta-testing
description: 为 OpenVetta 的功能变更、Bug 修复、重构、公共合同和 UI 交互设计、编写或审查测试;在决定测试范围、补充回归测试、覆盖用户常见使用流程或评估测试质量时使用。纯文档或没有运行时行为变化的编辑不使用。
---

# OpenVetta 测试设计与编写

## 目标

编写能在真实回归发生时可靠失败、能说明哪项用户行为或稳定合同被破坏、且维护成本与风险相称的测试。不要以覆盖代码行、内部调用次数或大量快照代替行为证据。

开始前完整阅读目标目录适用的最近一级 `AGENTS.md`、相关包 README、现有测试和实现。测试命令与门禁以仓库根 `AGENTS.md` 和 `docs/dev/quality-gates.md` 为准;更近一级规则可以收紧本 Skill。

## 先定义要证明的行为

编码前列出:

1. 本次会改变的用户可见行为或公共合同。
2. 必须保持的不变量、兼容路径、事件顺序和副作用。
3. 用户在受影响功能中最可能执行的常见使用操作路径或流程。
4. 风险最高的边界、失败、取消、重试、恢复或持久化场景。

把每个场景写成 `Given / When / Then`:

- `Given`:用户已有的状态、合理默认配置和必要前置条件。
- `When`:用户从正常入口执行的连续操作,而不是直接调用本应隐藏的内部函数。
- `Then`:用户可见结果、公开输出、持久化事实、外部副作用或明确错误。

只覆盖本次改动实际影响的连续步骤,不机械穷举全产品路径。若多个常用入口经过不同的状态、协议或持久化边界,且改动同时影响这些边界,应分别覆盖代表性流程。

## 按任务选择测试组合

- Bug 修复:先建立能在旧实现失败的最小复现或明确基线,再写回归测试;修复后还要运行受影响的常见用户流程测试。
- 新功能或行为变化:至少覆盖一条代表性常见用户流程,再为新增分支、关键边界及可恢复失败添加定向测试。
- 内部重构:明确保持的不变量,使用已有测试、差分测试或合同测试证明外部行为、事件顺序和副作用未变。
- 公共合同变更:检查生产者、消费者及兼容路径,覆盖 Schema、协议、序列化、错误和版本边界。
- UI 交互变化:覆盖真实渲染、事件接线、用户输入、提交、焦点、可访问语义、加载和错误恢复;纯函数测试不能替代组件测试。
- 权限、并发、取消、重试和生命周期:覆盖允许与拒绝、竞争顺序、清理、重复执行和恢复等会造成高后果的状态转换。

纯文档、无逻辑文案、类型转发或已由现有合同准确覆盖的机械改动可以不新增测试,但仍需运行适当验证并在交付中说明依据。

## 识别用户常见使用流程

“常见”指普通用户通过产品正常入口、在合理默认配置下完成目标,而不是测试专用入口、调试开关或罕见内部调用。根据功能实际支持的能力和本次改动选择流程,例如:

- 会话与 Agent:新建或恢复会话 → 输入任务或添加上下文 → 发送 → 查看流式回复或工具结果 → 继续追问、取消或重试 → 重新打开后看到正确历史。
- 工具与权限:触发需授权的操作 → 查看权限说明 → 批准或拒绝 → 操作继续或被阻止 → 收到明确结果和错误反馈。
- Plugin、Skill 与 MCP:安装或导入 → 校验清单与确认权限 → 启用 → 在会话中调用 → 禁用、更新或卸载后状态正确。
- 设置、Provider 与运行时:打开设置 → 修改并校验配置 → 保存 → 当前会话或重启后生效 → 配置无效或资源不可用时得到可恢复反馈。
- Agent Team:选择或创建团队 → 发起任务 → 成员执行并展示进度 → 汇总结果 → 继续协作、结束或恢复团队会话。
- 文件与项目:打开项目或选择文件 → 执行读取、编辑或生成操作 → 查看变更与状态 → 确认、重试或重新打开后结果一致。

这些是选路示例,不是要求每次修改都覆盖全部流程。优先从产品入口、用户文档、现有组件接线和生产调用链确认真实流程,不根据测试便利性发明产品行为。

## 选择最低但充分的测试层级

- 单元测试:纯计算、解析、选择、校验和状态转换。
- 组件测试:UI 渲染、输入、提交、焦点、可访问语义和异步反馈。
- 合同测试:公共 API、IPC/RPC、Tool/Prompt Schema、事件、序列化和跨包边界。
- 集成测试:跨模块流程、真实内部装配、持久化以及多个状态转换。
- E2E:只有真实浏览器、Electron、进程、文件系统、打包布局或网络边界无法由低层测试证明时使用。

用户流程测试从用户可触达入口或最接近该入口的组件、服务公共接口进入,尽量使用真实内部装配。能够由组件、合同或集成测试证明时,不要机械升级为 E2E。只有用户在当前任务中明确要求时才运行 `verify:ui:*`。

## 写出稳定且有诊断力的测试

- 测试名称描述用户行为或稳定合同及预期结果,例如“用户拒绝工具授权后不执行操作并看到已取消状态”。
- 使用 Arrange / Act / Assert 或 Given / When / Then,使前置状态、动作和结果清晰分离。
- 断言用户可见结果、公开返回值、持久化事实和必要副作用;不要锁定私有字段、脆弱 DOM 层级或偶然调用次数。
- 一个测试表达一个清晰场景,但允许验证同一用户流程中的连续状态,不要把流程拆成只能按顺序运行的多个测试。
- 对纯逻辑的多组边界输入优先使用表驱动测试;对用户流程保留可读的独立场景。
- 时间、随机数、并发和重试使用可控时钟、固定输入或显式同步点;不要用任意 `sleep` 等待。
- 每个测试独立建立和清理状态,释放计时器、订阅、进程、临时文件和数据库连接,不依赖执行顺序。
- 快照只用于稳定且整体形状本身就是合同的输出;不要用大面积快照掩盖关键断言。

## Mock 与测试替身

只在真实外部边界使用 Mock,例如 Provider、网络、操作系统、外部进程或不可控时间。内部协作者优先使用真实实现、内存存储或行为明确的 Fake。

不要把被测模块内部全部 Mock 后只验证调用:

```ts
expect(sendMessage).toHaveBeenCalledOnce();
```

应优先证明用户目标或稳定事实成立:

```ts
expect(screen.getByText("分析完成")).toBeVisible();
expect(await conversationStore.load(sessionId)).toContainEqual(
	expect.objectContaining({ text: "分析完成" }),
);
```

只有当“调用某个外部边界”本身就是公开合同或必要副作用时,才断言其参数和次数。

## 审查测试质量

审查新增或现有测试时逐项判断:

1. 若实现出现目标回归,这个测试是否真的会失败?
2. 是否覆盖了本次改动影响的代表性用户常见操作流程,而不只是孤立函数或异常分支?
3. 断言是否面向可观察行为和稳定合同?
4. Mock 是否只位于真实外部边界,是否意外绕过了关键生产接线?
5. 异步、时间、并发和资源清理是否确定且无任意等待?
6. 测试层级是否足够,又没有不必要地升级为缓慢 E2E?
7. 失败信息能否直接指出被破坏的行为?

发现缺口时,优先补能捕获真实回归的最低层测试,不重复相同断言,也不为了覆盖率数字制造低价值用例。

## 运行与交付

使用仓库统一入口,不使用裸 `bun test`、`bunx vitest`、`npx vitest` 或直接 `vitest`:

```bash
bun scripts/quality/run-vitest.mjs --run <test-file>
bun run test:pkg <name>
bun run test:changed
bun run check:quick
bun run check
```

选择与改动相称的最小充分范围:先跑定向测试,一轮编辑后跑 `check:quick`,涉及多个包或范围不明确时跑 `test:changed`,代码任务完成后跑一次 `check`。`check` 不运行测试,不能替代行为测试。

交付时说明覆盖了哪些用户流程或合同、实际运行了哪些测试与检查、哪些未运行及原因、剩余风险和兼容性影响。不得声称未执行的验证已经通过。

## Skill 维护

`.agents/skills/vetta-testing/SKILL.md` 是事实源。修改本 Skill 时同步更新 `.claude/skills/vetta-testing/SKILL.md`,并验证两份文件内容一致。

Discussion

Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.

Posts are public.Sign in to post

No one has posted yet. Be the first.