DeepChat 测试体系完全指南:作用域划分、命令矩阵与回归覆盖策略
【免费下载链接】deepchat🐬DeepChat - A smart assistant that connects powerful AI to your personal world项目地址: https://gitcode.com/GitHub_Trending/dee/deepchat
DeepChat 是一个连接多种 AI 能力与个人工作空间的 Electron 桌面 Agent 客户端。本指南以仓库中 test/README.md 为骨架,系统讲解 DeepChat 测试体系的作用域划分、可复制的命令矩阵、持久化回归覆盖策略、原生 SQLite ABI 注意事项以及手工 deeplink 验证流程,并结合package.json、vitest.config.ts、Playwright 配置与测试夹具源码,给出可直接落地的运行与维护方案。读完本文,你将能在本地精确地运行最小测试目标、理解各测试层的边界与取舍、遵守删除测试的准则,并在交付前正确执行完整质量门禁。
测试体系的定位:守护工作流与维护契约
DeepChat 的测试体系遵循一条核心原则:测试保护用户工作流(user workflows)与维护契约(maintained contracts),完成一个功能并不等于可以撤销其回归覆盖。也就是说,回归测试是功能交付的一部分,而不是可以随版本迭代随意裁剪的附属物。
这一理念与仓库的验证策略一脉相承:根据 docs/spec-driven-dev.md 中的"Implementation-First, Risk-Based Validation"原则,实现完成之后应运行最小相关的现有测试与静态/构建检查,并把能保护用户可见行为、跨模块契约、持久化与迁移、生命周期与并发、恢复、安全边界或已被证实的回归的最小测试提交为持久化回归保护(durable regression protection)。测试是验证机制之一,其存在目的是锁定契约与行为,而非追逐覆盖率数字。
因此,在 DeepChat 仓库中,任何测试的增删都应回答同一个问题:这条测试保护了哪个工作流或契约?删掉它之后,回归保护的空缺在哪里?
作用域与命令矩阵
所有测试命令都从仓库根目录执行,使用 pnpm,并遵循package.json中engines声明的 Node 版本。当前仓库声明为node >=24.18.0 <25、pnpm >=10.34.5 <11(见 package.json)。首次使用时先执行pnpm install安装依赖;当所选验证需要可选应用运行时(如 OCR、DuckDB VSS 等原生组件)时,再通过pnpm run installRuntime安装。
以下命令矩阵完整覆盖了 test/README.md 定义的全部作用域:
| 作用域 | 命令 | 边界 |
|---|---|---|
| Main 与 Renderer | pnpm test | vitest.config.ts中声明的 Vitest projects |
| Main、共享契约与脚本 | pnpm run test:main | Node 环境;test/main |
| Renderer | pnpm run test:renderer | Vue Test Utils 与 jsdom;test/renderer |
| Portable Memory(可移植内存) | pnpm run test:memory | 作用域校验、测试类型检查与可移植套件 |
| Electron smoke | pnpm run e2e:smoke | 构建后的应用;见 E2E 环境与隔离 |
| CI Electron 子集 | pnpm run e2e:smoke:ci | 启动与 Settings 导航 |
| 覆盖率 | pnpm run test:coverage | 报告输出到coverage/ |
| 交互式监听 | pnpm run test:watch | 显式 watch 模式 |
命令背后的实际配置
pnpm test即vitest run(见 package.json),由 vitest.config.ts 通过projects同时定义renderer与main两个 project:renderer 使用jsdom环境,includetest/renderer/**/*.{test,spec}.{js,ts};main 使用node环境,includetest/main/**/*.{test,spec}.{js,ts}。两个 project 都通过 alias 与 test/mocks/electron.ts 将electron、@electron-toolkit/utils替换为测试 mock。- 一个值得注意的细节:
electron-store11 是 ESM-only 包,会被外部化,因此配置中通过server.deps.inline: ['electron-store']将其内联,保证它内部的electronimport 解析到测试 mock 而非真实包。 - 覆盖率由 v8 provider 生成,
vitest.config.ts中全局阈值要求 branches / functions / lines / statements 均不低于 80%,报告目录为./coverage。 - Renderer 套件有独立配置 vitest.config.renderer.ts,同样使用 jsdom,
maxWorkers: 2(与主配置一致,TEST_MAX_WORKERS = 2),默认超时 10 秒。注释明确说明这是为了避免重负载的 jsdom/Markstream 套件在无约束时竞争 CPU 与 GC。 pnpm run test:memory是三段式组合命令(见 package.json):先跑test:memory:scope(执行 scripts/check-memory-test-scope.mjs 校验内存测试作用域清单),再跑test:memory:type(执行 scripts/typecheck-memory-tests.mjs 对内存测试做类型检查),最后执行vitest --config vitest.config.memory.ts --run运行可移植套件。- E2E 使用 Playwright:test/e2e/playwright.config.ts 设置
fullyParallel: false、workers: 1、用例超时 300 秒、断言超时 30 秒,失败时保留截图、视频与 trace,并约定data-testid作为测试标识属性。CI 子集则使用独立的 test/e2e/playwright.ci.config.ts。
只跑最小的相关目标
全量套件耗时较长,开发迭代期间应只运行与当前改动相关的最小目标。test/README.md 给出的两个典型示例:
pnpm exec vitest run --config vitest.config.ts test/main/session/lifecycle.test.ts pnpm exec vitest run --config vitest.config.renderer.ts test/renderer/stores/sessionStore.test.ts第一条直接指定主进程套件中的单个测试文件(走vitest.config.ts的 main project),第二条显式使用 renderer 配置运行单个 store 测试。对应 E2E 场景,也可以只跑一个确定的 Playwright 用例,例如:
pnpm exec playwright test -c test/e2e/playwright.config.ts 36-chat-streaming原生 SQLite 的 ABI 注意事项
Native SQLite 的 Node ABI 重建与必需的原生校验属于 CI 的职责,不要为了跑一个本地测试而重建共享依赖——那可能把 Electron ABI 覆盖掉,导致应用在真实环境中无法加载原生模块。
与此配套的机制是:原生与平台门控(platform-gated)的套件在前提条件不可用时可以跳过,且跳过(skipped)的测试不能作为该平台通过的证据。内存测试的作用域分类清单位于 test/memory-test-scope.json,它把test/main下的内存相关测试划分为四类:
behavior:纯行为测试,例如test/main/memory/retrievalService.test.ts、test/main/memory/writeCoordinator.test.ts、test/main/agent/deepchat/memory/memoryRuntimeCoordinator.test.ts;native:依赖原生 SQLite 的测试,例如test/main/memory/agentMemoryTable.test.ts、test/main/session/data/tapeLifecycle.test.ts;eval:评测类,例如test/main/memory/memoryRetrieval.eval.test.ts;perf:性能类,位于test/main/performance/memory/下,例如tapeScale.perf.ts、recallScale.perf.ts。
从 scripts/check-memory-test-scope.mjs 的源码可以看到归类逻辑:native类需要匹配itIfSqlite/describeIfSqlite这类门控调用(如test/main/session/data/tapeRecall.test.ts中大量使用itIfSqlite(...)),同时会校验清单中是否存在遗漏或多余条目,防止内存测试在不知不觉中被划出门禁。清单还包含exemptions字段,用于显式登记少数跨域套件留在完整 main 门禁中的理由。
持久化覆盖:测试保护的能力分组
test/README.md 用一张分组表概括了各测试目录守护的核心能力,这是理解整个测试体系布局的钥匙:
| 分组 | 受保护的能力 |
|---|---|
| Agent、session 与 Tape | 准入(admission)、取消、并发、恢复、投影(projection)与持久化历史 |
| Provider、ACP、MCP、tools 与 plugins | 协议兼容性、权限、凭据、生命周期与故障隔离 |
| Memory、storage、sync 与 import | 隔离、迁移、损坏恢复与持久化数据 |
| Desktop、preload、routes 与 renderer clients | 调用方授权、可序列化契约、事件投递与清理 |
| Renderer 组件、stores 与 composables | 键盘与焦点行为、无障碍内容、草稿、配置与异步状态 |
| 构建与脚本 | 包完整性、支持的目标平台、签名、更新器兼容性与必需 CI 门禁 |
| Electron smoke | 通过真实 renderer/preload/main 边界验证应用装配与工作流 |
这些分组与仓库目录一一对应:test/main/下按 agent、session、provider、mcp、memory、tool 等子目录组织;test/renderer/下对应 components、stores、composables;E2E 的 38 个 spec 文件(test/e2e/specs/)则从01-launch到37-accessibility依次覆盖启动、Settings 导航、typed IPC 边界、workspace watcher、provider 配置、Agent 与插件管理、composer 草稿、本地流式生成以及无障碍检查等真实工作流。
测试策略:保留真实域逻辑,替代昂贵边界
test/README.md 给出三条明确的策略准则:
- 在能提供有效行为覆盖的地方保留真实的域逻辑、store 与临时文件;
- 替代网络、模型、操作系统以及其他昂贵或不确定(nondeterministic)的边界;
- 当交互本身就是契约时(例如授权、幂等性、协议调用),交互断言才是恰当的。
E2E 层是这条策略的典型体现:根据 test/e2e/README.md,默认的 smoke 套件覆盖启动、Settings 导航、typed IPC 边界、workspace watchers、provider 配置、Agent 与插件管理、composer 草稿和本地流式生成;其中插件与流式 spec 通过真实应用路由使用 loopback HTTP/MCP 夹具,不需要任何外部 provider 凭据。只有 5 个 spec(基础对话、会话持久化、provider 连通性、聊天滚动归属、composer 宽度)要求RUN_PROVIDER_INTEGRATION=true,其默认 provider/model 为minimax/MiniMax-M2.7,可用DEEPCHAT_E2E_PROVIDER_ID与DEEPCHAT_E2E_MODEL_ID覆盖(默认值定义见 test/e2e/helpers/testData.ts)。
jsdom 的边界要认清
jsdom 并不能验证原生窗口焦点、屏幕阅读器语音或计算后的视觉布局,这些行为必须使用适当的 Electron 或人工验收方式验证。同时应避免向提交的套件中添加空示例、临时探针或镜像实现(implementation-mirroring)的测试。
删除测试的准则
删除一条测试必须有证据,且证据必须属于以下四类之一:
- 测试已过时(obsolete);
- 测试是空洞的(vacuous),即断言无法失败;
- 测试完全冗余(fully redundant);
- 测试只是锁定了偶然的实现选择(incidental implementation choice)。
在删除之前,先明确它原本提供的保护,或精确指出覆盖空缺。同时,test/README.md 明确反对以下做法:
- 为散文(prose)、退役的迁移文件名或私有赋值计数保留源码字符串检查(source-string checks);
- 源码检查仍然有价值的情况仅限于:强制的导入边界、有文档记载的视觉/启动回归,以及机器可读的打包或工作流契约。
断言偏好与证据质量
倾向断言以下可验证的结果,而非实现细节:
- 公开结果(public results);
- 持久化数据;
- 发出的事件(emitted events);
- 渲染后的语义(rendered semantics);
- 可恢复的错误(recoverable errors)。
这一偏好与 docs/spec-driven-dev.md 中"不要保留那些仅仅镜像私有控制流、断言偶然的调用顺序、通过 mock 复制实现或只为了提升覆盖率而存在的测试"的约束一脉相承:宁可不新增测试,也不要新增一个低价值的、与实现耦合的测试。
交付前的质量门禁
在交接(handoff)之前,必须按顺序运行:
pnpm run format pnpm run i18n pnpm run lint pnpm run typecheck以及相关的测试。这些门禁在 package.json 中均有对应实现:
format使用oxfmt .(另有format:check用于只检查不改动);i18n由i18n:validate(scripts/validate-i18n.mjs)与i18n-check -s zh-CN组成,检查src/renderer/src/i18n的语言键一致性;lint是三个步骤的组合:lint:agent-cleanup(scripts/agent-cleanup-guard.mjs)、lint:alert-dialog-contract(scripts/alert-dialog-contract-guard.mjs)以及oxlint .;typecheck分为typecheck:node(tsc -p tsconfig.node.json)与typecheck:web(vue-tsc -p tsconfig.app.json)两段。
有一个容易被忽略的坑需要特别注意:应用的类型检查并不会自动类型检查每一个测试文件,类型层面的断言(type-only assertions)需要显式的类型检查目标才能提供证据。这正是test:memory:type(typecheck-memory-tests.mjs)存在的意义——内存测试的类型正确性需要单独验证。
手工 deeplink 验证
除自动化测试外,test/README.md 还定义了手工验证 deeplink 的流程。在 DeepChat 运行时,用浏览器打开 test/manual/deeplink-playground.html,页面会为三类 deeplink 提供假的 payload 示例,并允许浏览器唤起deepchat://协议(部分环境会先弹出确认)。
三类 deeplink 及其在页面中的实现(见 test/manual/deeplink-playground.html 的脚本部分):
deepchat://start:唤起应用并把预置消息、模型或 mentions 带入新会话。payload 支持msg、model、system、mentions字段,链接格式为deepchat://start?msg=...&model=...;deepchat://mcp/install:安装 stdio / sse MCP 配置。payload 为mcpServers对象(command+args或url+type),当前协议参数名是code,值是对 payload JSON 做 Base64 编码后再 URL 编码的结果;deepchat://provider/install:导入 provider。payload 区分 built-in 与 custom 两种语义:built-in 用id匹配并覆盖,custom 用name + type新增;built-in 导入列表已排除acp(它不属于settings-provider导入流)。链接格式为deepchat://provider/install?v=1&data=<Base64(JSON)>。
页面还内置了一个 provider 链接构造器(builder),可临时修改id / name / type / baseUrl / apiKey,实时生成符合应用解析格式的 deeplink,方便外部联调。
小结:从命令到策略的一体化测试体系
DeepChat 的测试体系是"命令矩阵 + 分层策略 + 契约守护"的一体化工程:命令层覆盖 main/renderer/memory/e2e 四大作用域并提供最小目标运行方式;策略层强调保留真实域逻辑、替代昂贵边界、以契约为准;治理层通过memory-test-scope.json、作用域校验脚本与严格的删除准则防止测试体系腐化;交付层则以 format、i18n、lint、typecheck 四道门禁兜底。对参与 DeepChat 开发的工程师而言,把 test/README.md 中的命令矩阵与准则内化,就等于拿到了这套 Electron + Vitest + Vue Test Utils + Playwright 混合测试栈的正确使用方式。
【免费下载链接】deepchat🐬DeepChat - A smart assistant that connects powerful AI to your personal world项目地址: https://gitcode.com/GitHub_Trending/dee/deepchat
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考