testing

testing

热门

Vitest 测试指南。适用于编写或更新测试用例、修复失败测试、提升代码覆盖率、排查测试问题以及配置 Mock。

8.1万Star
1.6万Fork
更新于 2026/8/7
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

核心原则

  1. 优先使用 vi.spyOn 而非 vi.mock —— 针对性更强,更易维护
  2. 测试必须通过类型检查 —— 编写测试后运行 bun run type-check
  3. 连续尝试 1-2 次修复仍挂掉时,及时停下寻求帮助
  4. 测试业务行为,而非实现细节
  5. 为 Bug 修复补充回归测试 —— 修复 Bug 后,添加一个在修复前失败、修复后通过的回归测试,防止问题复发
  6. 不要新增组件测试 —— 只更新现有的 React 组件测试。复杂的逻辑应抽离到 Hook 中独立测试
  7. 改完所有源码后再动测试 —— 先完成全部源文件的修改,再集中更新测试。交替修改会干扰对源码变更的思考逻辑,在涉及多文件修改时尤为明显

基础测试结构

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) → 更新预期字符串

删除(过度定义,价值较低)

  • 参数透传测试:严格校验内部函数调用参数的测试(例如 expect(internalFn).toHaveBeenCalledWith(expect.objectContaining({ exact params })))—— 这类测试每次重构都会挂,且与行为测试重复覆盖。
  • 与实现强绑定的测试:验证代码内部 如何运作 而非 产生了什么 的测试。如果上层测试已经覆盖了相同的行为,底层测试只会增加维护成本,而不会带来覆盖率收益。

决策清单

  1. 该测试是否验证了外部可观测的行为(API 响应、数据库写入、渲染输出)?→ 保留
  2. 该测试是否仅校验内部链路调用的细节(哪个函数接收了什么参数)?→ 检查行为测试是否已覆盖。如果是 → 删除
  3. 相同的行为是否已在更高层级的集成测试中覆盖?→ 删除低层级的重复测试
  4. 下一次日常重构时,该测试是否还会再次挂掉?→ 考虑提升至集成测试层级或直接删除

编写新测试时的建议

  • 优先使用集成层级的断言(验证最终输出),而非内部调用的断言(验证内部传递参数)
  • 仅对稳定、公开的对外契约使用 expect.objectContaining —— 不要用于随重构变化的内部参数结构
  • 在边界处进行 Mock(数据库、网络、外部服务),不要在内部模块之间过度 Mock

常见问题

  1. 模块污染:当测试出现莫名其妙的失败时,尝试使用 vi.resetModules()
  2. Mock 未生效:检查 Mock 的放置位置,并在 beforeEach 中使用 vi.clearAllMocks()
  3. 测试数据污染:在 beforeEach/afterEach 中清理数据库状态
  4. 异步问题:在 React Hooks 测试中,将状态变更包裹在 act()