ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Archon 项目的 Bun 测试约定:mock.module() 污染防护与测试分批隔离实战指南

Archon 项目的 Bun 测试约定:mock.module() 污染防护与测试分批隔离实战指南 Archon 项目的 Bun 测试约定mock.module() 污染防护与测试分批隔离实战指南【免费下载链接】context-engineering-introContext engineering is the new vibe coding - its the way to actually make AI coding assistants work. Claude Code is the best for this so thats what this repo is centered around, but you can apply this strategy with any AI coding assistant!项目地址: https://gitcode.com/gh_mirrors/co/context-engineering-intro导读本文以 WISC 框架Write / Isolate / Select / CompressTier 2 按需加载规则中的testing.md为核心系统讲解 Archon远程 Agent 编码平台Bun TypeScript monorepo的 Bun 测试约定。你将掌握mock.module()进程级污染的成因与规避策略、按包分批运行测试的隔离方法、懒加载 Logger 与数据库依赖的可恢复 mock 模式以及一套可直接复制的标准测试结构模板——这些内容全部来自仓库内真实规则文件并交叉引用了执行与验证命令作为佐证。一、背景testing.md 在 WISC 框架中的定位1.1 WISC 是什么WISC 是一套管理 AI 编码会话上下文的实践框架四个字母分别代表字母含义作用W - Write将 Agent 的记忆外化到文件使记忆在上下文重置后仍然存活I - Isolate使用子 Agent 隔离研究噪音保持主会话上下文干净S - Select只加载当前任务需要的上下文避免全量加载C - Compress会话过长时聚焦压缩或交接作为安全网兜底排序刻意设计Write 与 Isolate 影响最大Select 是杠杆Compress 是兜底。该框架基于三层上下文系统落地详见 README。1.2 Tier 2按需自动加载的规则文件WISC 三层上下文系统的第二层是.claude/rules/下的按需规则。每个规则文件通过 YAML frontmatter 中的paths:声明触发路径——Agent 触碰匹配路径的文件时规则自动载入--- paths: - **/*.test.ts - **/*.spec.ts ---本文的主角testing.md位于 use-cases/ai-coding-wisc-framework/.claude/rules-example/testing.md就是这样一个按需规则当 Agent 开始编写或修改**/*.test.ts/**/*.spec.ts测试文件时整套 Bun 测试约定会自动进入上下文从源头避免写出带污染隐患的测试。二、核心难点mock.module() 的进程级污染2.1 问题的本质在 Bun 中mock.module()永久性地替换进程级process-wide模块缓存中的模块。这意味着替换一旦发生同一次bun test进程内所有后续文件都会读到被替换的版本mock.restore()无法撤销mock.module()的效果这一行为已由 Bun 官方 issue #7823 确认请以官方 tracker 为准而spyOn()的spy.mockRestore()可以正常恢复spy。这是整个测试约定最关键的认知mock.module()与spyOn()的恢复语义完全不同混淆二者是测试污染的第一大来源。2.2 三条铁律永远不要为mock.module()调用添加afterAll(() mock.restore())——它没有任何效果只会让人误以为恢复了同一bun test调用中绝不允许两个测试文件用不同实现mock.module()同一个路径——后者必然被前者污染内部模块优先使用spyOn()——spy.mockRestore()对 spy 是真实有效的。2.3 正确写法对照// CORRECT: spy可恢复 import * as git from archon/git; const spy spyOn(git, checkout); spy.mockImplementation(async () ({ ok: true, value: undefined })); // afterEach: spy.mockRestore(); // CORRECT: mock.module() 用于外部依赖不可恢复——隔离到独立测试文件 mock.module(slack/bolt, () ({ App: mock(() mockApp), LogLevel: { INFO: info } }));原则概括内部模块同仓库archon/*用 spy外部依赖第三方包用mock.module()并配合文件级隔离。三、测试分批按包隔离 bun test 调用3.1 为什么必须分批由于mock.module()的污染是进程级的最直接的规避手段就是不让会互相污染的测试文件出现在同一次bun test进程中。Archon 的做法是每个包package把测试拆成多次独立的bun test调用。包批次数量批次构成示例archon/core7 批clients、handlers、dbutils、path-validation、cleanup-service、title-generator、workflows、orchestratorarchon/workflows5 批—archon/adapters3 批chatcommunityforge-auth、github-adapter、github-contextarchon/isolation3 批—分批粒度取决于包内模块的 mock 依赖关系共享同一mock.module()路径的测试会被归入不同批次。3.2 正确的运行命令绝不从仓库根目录直接运行bun test——那样会把所有包的测试合并进一个进程触发约 135 个 mock 污染失败。正确姿势bun run test # 正确通过 bun --filter * test 实现按包隔离 bun run test --watch # 监听模式针对单个包bun run test在仓库根目录执行时会借助bun --filter * test逐个包分别启动测试进程从而保证mock.module()的污染被批次边界天然隔绝。3.3 新增测试文件时的归属判定新增测试文件前先确认它属于哪个批次如果它的mock.module()实现与同批次既有文件冲突就必须在对应包的package.json测试脚本中新建一个批次而不是塞进已有批次。四、懒加载 Logger 的 mock 模式Archon 中所有 adapter / db / orchestrator 文件都使用懒加载 logger模式createLogger在函数内部按需初始化而非模块顶层。这带来一个测试前提mock 必须先于被测模块的 import 执行。// MUST come before import of the module under test const mockLogger { fatal: mock(() undefined), error: mock(() undefined), warn: mock(() undefined), info: mock(() undefined), debug: mock(() undefined), trace: mock(() undefined), }; mock.module(archon/paths, () ({ createLogger: mock(() mockLogger) })); import { SlackAdapter } from ./adapter; // Import AFTER mock要点mock.module(archon/paths, ...)必须出现在import { SlackAdapter }之前否则模块缓存已建立mock 不生效六个日志级别fatal / error / warn / info / debug / trace全部 mock避免真实 logger 在测试中输出或依赖外部资源这一模式与execute.md中强调的懒加载 logger 约定use-cases/ai-coding-wisc-framework/.claude/commands/execute.md互为表里源码侧懒加载是为了让测试侧 mock 先于 import 生效。五、数据库测试 Mock 模式5.1 基础设施仓库提供两个测试专用工具来自../test/mocks/database即测试目录下的 mockscreateQueryResult(rows)构造带类型的结果对象模拟pool.query的返回值mockPostgresDialect模拟数据库方言对象。import { createQueryResult, mockPostgresDialect } from ../test/mocks/database; const mockQuery mock(() Promise.resolve(createQueryResult([]))); mock.module(./connection, () ({ pool: { query: mockQuery }, getDialect: () mockPostgresDialect, })); // In tests: mockQuery.mockResolvedValueOnce(createQueryResult([existingRow])); mockQuery.mockClear(); // in beforeEach5.2 与 database.md 的呼应这一 mock 模式与 Tier 2 的另一个规则文件 database.md 严格对应生产代码通过IDatabase接口统一访问数据库PostgreSQL 与 SQLite 自动探测pool.query与getDialect()是唯一入口。因此测试侧只需替换./connection模块的pool与getDialect就能让全部 DB 查询走 mock无需触碰真实数据库——这正是 database.md 中单元测试绝不使用真实数据库/文件系统约束的实现方式。配合mockResolvedValueOnce可以逐次注入不同行数据模拟空结果、命中一行、命中多行等场景并用mockClear()在beforeEach中清零调用计数。六、标准测试结构模板所有 Archon 测试遵循统一骨架从bun:test导入describe / test / expect / mock / beforeEach / afterEachbeforeEach清零调用计数断言既校验结果也校验调用次数import { describe, test, expect, mock, beforeEach, afterEach } from bun:test; describe(ComponentName, () { beforeEach(() { mockFn.mockClear(); // Reset call counts }); test(does thing when condition, async () { mockQuery.mockResolvedValueOnce(createQueryResult([fixture])); const result await functionUnderTest(input); expect(result).toEqual(expected); expect(mockQuery).toHaveBeenCalledTimes(1); }); });编写要点每个describe以组件/模块命名保持测试与源码结构对齐beforeEach统一mockClear()防止用例间调用计数串扰断言同时检查返回值toEqual与副作用toHaveBeenCalledTimes确保 mock 被真正调用且只调用一次。七、反模式清单Do Not完整背诵以下五条禁止可避免绝大多数测试污染问题禁止在完成所有mock.module()调用之前 import 被测模块的依赖——mock 必须在 import 之前生效禁止为mock.module()使用afterAll(() mock.restore())——它静默地什么都不做禁止在单元测试中使用真实数据库或文件系统——一律 mock禁止从仓库根目录运行bun test——必须走bun run test按包分批禁止向已有批次添加mock.module()实现冲突的新测试文件——应在新批次中创建。八、与执行流程的交叉印证8.1 execute.md 中的测试指导execute.md 在测试如需添加一节与 testing.md 完全对齐先确认新测试文件在包的package.json中归属哪个批次mock.module()在 Bun 中是永久性的——新测试文件要放置在不污染其他文件的位置其他测试文件也会直接使用的模块用spyOn()而非mock.module()。8.2 完整的验证命令链当改动涉及测试时执行计划会按以下顺序逐步验证最终以bun run validate收尾bun run type-check # 全包类型检查 bun run lint # 零警告策略 bun run format:check # 格式检查 bun run test # 按包分批测试勿从根目录直接跑 bun run validate # 等价于 type-check lint --max-warnings 0 format:check test其中bun run test正是 testing.md 强调的按包隔离入口四道关卡全部通过才算完成。九、落地到你的项目把 testing.md 的约定迁移到其他 Bun TypeScript monorepo 时按以下顺序落地先立认知向团队明确mock.module()是进程级永久替换、mock.restore()救不了它spy 才可恢复按包分批检查各包package.json的 test 脚本把共享mock.module()路径的测试拆到不同bun test进程统一入口根目录 test 脚本用bun --filter * test逐包隔离禁止裸bun test固化模板把mock 先于 import spyOn 恢复 createQueryResult 注入 beforeEach 清零沉淀为标准测试骨架禁止清单入规则将第七节反模式写入自己的testing.md规则文件让 Agent 在触碰测试文件时自动获得这些约束。参考文件本文核心.claude/rules-example/testing.mdWISC 框架总览与 Tier 2/3 说明README.md执行计划中的测试约定与验证命令.claude/commands/execute.md数据库约定IDatabase 接口、dialect、mock 依据.claude/rules-example/database.md依赖层与包结构背景prime 输出约定.claude/commands/prime.md【免费下载链接】context-engineering-introContext engineering is the new vibe coding - its the way to actually make AI coding assistants work. Claude Code is the best for this so thats what this repo is centered around, but you can apply this strategy with any AI coding assistant!项目地址: https://gitcode.com/gh_mirrors/co/context-engineering-intro创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表