news 2026/9/18 9:16:56

DeepChat 测试体系完全指南:作用域划分、命令矩阵与回归覆盖策略

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DeepChat 测试体系完全指南:作用域划分、命令矩阵与回归覆盖策略

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.jsonvitest.config.ts、Playwright 配置与测试夹具源码,给出可直接落地的运行与维护方案。读完本文,你将能在本地精确地运行最小测试目标、理解各测试层的边界与取舍、遵守删除测试的准则,并在交付前正确执行完整质量门禁。

测试体系的定位:守护工作流与维护契约

DeepChat 的测试体系遵循一条核心原则:测试保护用户工作流(user workflows)与维护契约(maintained contracts),完成一个功能并不等于可以撤销其回归覆盖。也就是说,回归测试是功能交付的一部分,而不是可以随版本迭代随意裁剪的附属物。

这一理念与仓库的验证策略一脉相承:根据 docs/spec-driven-dev.md 中的"Implementation-First, Risk-Based Validation"原则,实现完成之后应运行最小相关的现有测试与静态/构建检查,并把能保护用户可见行为、跨模块契约、持久化与迁移、生命周期与并发、恢复、安全边界或已被证实的回归的最小测试提交为持久化回归保护(durable regression protection)。测试是验证机制之一,其存在目的是锁定契约与行为,而非追逐覆盖率数字。

因此,在 DeepChat 仓库中,任何测试的增删都应回答同一个问题:这条测试保护了哪个工作流或契约?删掉它之后,回归保护的空缺在哪里?

作用域与命令矩阵

所有测试命令都从仓库根目录执行,使用 pnpm,并遵循package.jsonengines声明的 Node 版本。当前仓库声明为node >=24.18.0 <25pnpm >=10.34.5 <11(见 package.json)。首次使用时先执行pnpm install安装依赖;当所选验证需要可选应用运行时(如 OCR、DuckDB VSS 等原生组件)时,再通过pnpm run installRuntime安装。

以下命令矩阵完整覆盖了 test/README.md 定义的全部作用域:

作用域命令边界
Main 与 Rendererpnpm testvitest.config.ts中声明的 Vitest projects
Main、共享契约与脚本pnpm run test:mainNode 环境;test/main
Rendererpnpm run test:rendererVue Test Utils 与 jsdom;test/renderer
Portable Memory(可移植内存)pnpm run test:memory作用域校验、测试类型检查与可移植套件
Electron smokepnpm run e2e:smoke构建后的应用;见 E2E 环境与隔离
CI Electron 子集pnpm run e2e:smoke:ci启动与 Settings 导航
覆盖率pnpm run test:coverage报告输出到coverage/
交互式监听pnpm run test:watch显式 watch 模式

命令背后的实际配置

  • pnpm testvitest run(见 package.json),由 vitest.config.ts 通过projects同时定义renderermain两个 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: falseworkers: 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.tstest/main/memory/writeCoordinator.test.tstest/main/agent/deepchat/memory/memoryRuntimeCoordinator.test.ts
  • native:依赖原生 SQLite 的测试,例如test/main/memory/agentMemoryTable.test.tstest/main/session/data/tapeLifecycle.test.ts
  • eval:评测类,例如test/main/memory/memoryRetrieval.eval.test.ts
  • perf:性能类,位于test/main/performance/memory/下,例如tapeScale.perf.tsrecallScale.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-launch37-accessibility依次覆盖启动、Settings 导航、typed IPC 边界、workspace watcher、provider 配置、Agent 与插件管理、composer 草稿、本地流式生成以及无障碍检查等真实工作流。

测试策略:保留真实域逻辑,替代昂贵边界

test/README.md 给出三条明确的策略准则:

  1. 在能提供有效行为覆盖的地方保留真实的域逻辑、store 与临时文件
  2. 替代网络、模型、操作系统以及其他昂贵或不确定(nondeterministic)的边界
  3. 当交互本身就是契约时(例如授权、幂等性、协议调用),交互断言才是恰当的

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_IDDEEPCHAT_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用于只检查不改动);
  • i18ni18n: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:nodetsc -p tsconfig.node.json)与typecheck:webvue-tsc -p tsconfig.app.json)两段。

有一个容易被忽略的坑需要特别注意:应用的类型检查并不会自动类型检查每一个测试文件,类型层面的断言(type-only assertions)需要显式的类型检查目标才能提供证据。这正是test:memory:typetypecheck-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 支持msgmodelsystemmentions字段,链接格式为deepchat://start?msg=...&model=...
  • deepchat://mcp/install:安装 stdio / sse MCP 配置。payload 为mcpServers对象(command+argsurl+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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 9:14:07

支付清算全解析:从支付发起到资金到账的底层逻辑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 9:05:26

嵌入式工控机选型:五大工业指标与采购验收指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 9:02:58

mysql-for-visualstudio安装与实战:打通Visual Studio与MySQL连接

做 .NET 开发的人&#xff0c;尤其是项目里数据库选了 MySQL 的&#xff0c;大概率都撞过这种尴尬&#xff1a;Visual Studio 里默认数据源只有 SQL Server&#xff0c;想直接连个 MySQL 表看看数据&#xff0c;右键“添加连接”翻遍列表也找不到 MySQL 的影子。这时你会意识到…

作者头像 李华
网站建设 2026/9/18 9:02:30

接口压力测试实战:从工具选型到容量规划与瓶颈定位

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 9:02:07

将 24.4pp 差距压到 12.0pp,TaoToken 改 GPT-4.1 智能体 Key

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华