SKILL.md
只读
名称
testing
描述
Vitest 测试指南。适用于编写或更新测试用例、修复失败测试、提升代码覆盖率、排查测试问题以及配置 Mock。
LobeHub 测试指南
快速参考
常用命令:
# 运行指定的测试文件
bunx vitest run --silent='passed-only' '[file-path]'
# 数据库包(client-db,PGlite —— 默认配置,跳过 BM25/pg_search)
cd packages/database && bunx vitest run --silent='passed-only' '[file]'
# 数据库包(server-db,Postgres —— 包含 BM25/pgvector 对齐测试,CI 覆盖率以此为准)
cd packages/database && TEST_SERVER_DB=1 bunx vitest run --silent='passed-only' '[file]'
切勿运行 bun run test —— 它会跑完全部 3000+ 个测试(耗时约 10 分钟)。
数据库 model/repository 注意事项: 在
packages/database/src/models/**
或src/repositories/**下新增的每个文件,都必须在同一个 PR 中附带同级的__tests__/<name>.test.ts测试文件。
请通过getTestDB()使用真实数据库(集成测试风格),对 BM25/全文搜索相关的测试块添加describe.skipIf(!isServerDB)防护,并始终对用户数据隔离进行测试。有关环境搭建、Schema 注意事项以及 client-vs-server-db 的划分,详见references/db-model-test.md。
测试分类
| 分类 | 位置 | 配置文件 |
|---|---|---|
| Webapp | src/**/*.test.ts(x) |
vitest.config.ts |
| Packages | packages/*/**/*.test.ts |
packages/*/vitest.config.ts |
| Desktop | apps/desktop/**/*.test.ts |
apps/desktop/vitest.config.ts |
核心原则
- 优先使用
vi.spyOn而非vi.mock—— 针对性更强,更易维护 - 测试必须通过类型检查 —— 编写测试后运行
bun run type-check - 连续尝试 1-2 次修复仍挂掉时,及时停下寻求帮助
- 测试业务行为,而非实现细节
- 为 Bug 修复补充回归测试 —— 修复 Bug 后,添加一个在修复前失败、修复后通过的回归测试,防止问题复发
- 不要新增组件测试 —— 只更新现有的 React 组件测试。复杂的逻辑应抽离到 Hook 中独立测试
- 改完所有源码后再动测试 —— 先完成全部源文件的修改,再集中更新测试。交替修改会干扰对源码变更的思考逻辑,在涉及多文件修改时尤为明显
基础测试结构
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
vi.restoreAllMocks();
});
describe('ModuleName', () => {
describe('functionName', () => {
it('should handle normal case', () => {
// Arrange → Act → Assert
});
});
});
Mock 模式
// ✅ Mock 直接依赖项时使用 Spy
vi.spyOn(messageService, 'createMessage').mockResolvedValue('id');
// ✅ Browser API 使用 vi.stubGlobal
vi.stubGlobal('Image', mockImage);
vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:mock');
// ❌ 避免在全局范围 Mock 整个模块
vi.mock('@/services/chat'); // 粒度过粗
详细指南
针对具体的测试场景,请参阅 references/ 目录下的文档:
- 数据库 Model 测试:
references/db-model-test.md - Electron IPC 测试:
references/electron-ipc-test.md - Zustand Store Action 测试:
references/zustand-store-action-test.md - Agent Runtime E2E 测试:
references/agent-runtime-e2e.md - Desktop Controller 测试:
references/desktop-controller-test.md
修复失败的测试 —— 优化还是删除?
当测试因重构或实现变更(非 Bug)而失败时,在盲目修改前先做如下评估:
保留并修复(更新测试数据/断言)
- 行为测试:验证代码 做了什么 的测试(输出结果、副作用、用户可见的行为)。只需更新 Mock 数据格式或预期值即可。
- 示例:Tool 的数据结构从
{ name }变更为{ function: { name } }→ 更新 Mock 数据 - 示例:输出格式从
Current date: YYYY-MM-DD变更为Current date: YYYY-MM-DD (TZ)→ 更新预期字符串
- 示例:Tool 的数据结构从
删除(过度定义,价值较低)
- 参数透传测试:严格校验内部函数调用参数的测试(例如
expect(internalFn).toHaveBeenCalledWith(expect.objectContaining({ exact params })))—— 这类测试每次重构都会挂,且与行为测试重复覆盖。 - 与实现强绑定的测试:验证代码内部 如何运作 而非 产生了什么 的测试。如果上层测试已经覆盖了相同的行为,底层测试只会增加维护成本,而不会带来覆盖率收益。
决策清单
- 该测试是否验证了外部可观测的行为(API 响应、数据库写入、渲染输出)?→ 保留
- 该测试是否仅校验内部链路调用的细节(哪个函数接收了什么参数)?→ 检查行为测试是否已覆盖。如果是 → 删除
- 相同的行为是否已在更高层级的集成测试中覆盖?→ 删除低层级的重复测试
- 下一次日常重构时,该测试是否还会再次挂掉?→ 考虑提升至集成测试层级或直接删除
编写新测试时的建议
- 优先使用集成层级的断言(验证最终输出),而非内部调用的断言(验证内部传递参数)
- 仅对稳定、公开的对外契约使用
expect.objectContaining—— 不要用于随重构变化的内部参数结构 - 在边界处进行 Mock(数据库、网络、外部服务),不要在内部模块之间过度 Mock
常见问题
- 模块污染:当测试出现莫名其妙的失败时,尝试使用
vi.resetModules() - Mock 未生效:检查 Mock 的放置位置,并在 beforeEach 中使用
vi.clearAllMocks() - 测试数据污染:在 beforeEach/afterEach 中清理数据库状态
- 异步问题:在 React Hooks 测试中,将状态变更包裹在
act()中






