mcpField
A
accelint-ts-testing

accelint-ts-testing

github.com/gohypergiant测试与质量25347 次安装2 周前更新
代码仓库

关于 accelint-ts-testing

为编写易维护的 Vitest 测试提供详细指导,涵盖组织结构、隔离、断言、测试替身、异步场景和基于属性的测试。

主要功能

  1. 01清晰组织测试结构
  2. 02确保测试隔离且独立
  3. 03使用精确断言
  4. 04审慎选择测试替身
  5. 05覆盖异步与属性测试

适用场景

  1. 01设计新的 Vitest 测试套件
  2. 02检查脆弱的测试断言
  3. 03改进测试隔离与配置

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/gohypergiant/agent-skills --skill accelint-ts-testing

安装后在 Claude Code 中输入/accelint-ts-testing确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

AGENTS.md
README.md
assets/
output-report-template.md
references/
aaa-pattern.md
assertions.md
async-testing.md
error-handling.md
organization.md
parameterized-tests.md
performance.md
property-based-testing.md
quick-start.md
snapshot-testing.md
test-doubles.md
vitest-features.md

详细介绍

Vitest 最佳实践

编写易于维护且有效的 Vitest 测试的全面模式。重点介绍测试组织、清晰度和性能方面的专家级指导。

编写 Vitest 测试时绝对不要做的事

  • 绝不要为没有行为的文件编写测试 - 常量文件(只是 export const X = value)、类型定义文件、GLSL uniform 声明和纯数据文件不包含可测试的逻辑。测试 expect(MY_CONSTANT).toBe(42) 什么也验证不了:如果值发生变化,测试也会随之改变,提供零保护。这些"测试"浪费 CI 时间,并在值变化时造成维护负担。测试行为(函数、逻辑、转换),而不是数据声明。如果文件只导出类型、常量或数据结构而没有函数或逻辑,则完全跳过对其的测试。
  • 绝不要跳过全局 mock 清理配置 - 手动清理看似安全,但会造成"远距离作用"的失败:测试文件 A 中的 mock 泄漏到 3 个文件之后运行的测试文件 B 中,导致只有在特定顺序运行测试时才出现的非确定性失败。这些海森堡 bug 会在 CI 调试中浪费数小时。在 vitest.config.ts 中一次性配置 clearMocks: true、mockReset: true、restoreMocks: true,即可消除这一整类顺序相关的失败。
  • 绝不要让 describe 块嵌套超过 2 层 - 深层嵌套会造成认知负担和过多的缩进。把上下文放在测试名称中:it('should add item to empty cart') 对比 describe('when cart is empty', () => describe('addItem', ...))。
  • 绝不要编写读起来不像句子的测试描述 - 测试描述必须以小写形式补全"it ..."这句话。应写 it('should add item to cart'),而不是 it('Add item to cart') 或 it('It should add item to cart')。当加上"it"前缀时,描述读起来像一句话:"it should add item to cart"。首字母大写、像 it('addToCart test') 这样的非句子格式,或冗余的"It should",都会破坏可读性和测试输出的一致性。基于示例的测试使用 it('should...') 格式,而基于属性的测试使用 it('property: ...') 格式。
  • 绝不要测试库自身已经测试过的库内部实现 - 测试 expect(array.map(fn)).toEqual(expected) 是在浪费时间验证 Array.prototype.map 是否正确工作。JavaScript/TypeScript 标准库和成熟的第三方库已经被充分测试。将测试聚焦于你的业务逻辑,而不是证明 lodash、React 或语言本身能正常工作。如果你发现自己在测试"这个库函数是否做到了它声称的事?",那你测试的是错误的层面。测试你的代码如何使用库,而不是库是否能工作。
  • 绝不要仅仅为了测试而导出内部函数 - 测试应通过公共 API 验证行为,而不是深入实现细节。仅仅为了便于测试而导出私有辅助函数、内部工具或实现函数,是一种代码异味,它表明:(1) 公共 API 不足以测试该行为,或 (2) 测试验证的是实现细节而非行为。如果内部逻辑复杂到需要专门测试,就把它提取到一个独立的模块中,拥有自己的公共 API 和测试文件。私有函数通过调用它们的公共函数被间接测试。
  • 绝不要 mock 你自己的纯函数 - Mock 内部代码会让测试变得脆弱且价值降低。只 mock 外部依赖(API、数据库、第三方库)。优先使用 fake > stub > spy > mock。
  • 绝不要使用像 toBeTruthy() 或 toBeDefined() 这样的宽松断言 - 这些断言会为你从未打算的多个不同值通过:toBeTruthy() 对 1、"false"、[] 和 {} 都通过——它们在语义上都不同。当重构把 getUser() 从返回 {id: 1} 改为返回 1 时,你的测试仍然通过,但你的生产代码却出错了。宽松断言制造出的虚假信心会在生产环境中蒸发。toBeTypeOf() 不是宽松断言。
  • 绝不要测试实现细节而不是行为 - 验证"函数 X 被调用了 3 次"的测试会造成虚假的失败:你通过记忆化优化代码使其只调用 X 一次,所有测试都失败,然而用户体验完全相同(而且更快)。这些测试实际上在惩罚性能改进和重构。测试用户观察到的东西(给定输入对应的输出),而不是你的代码内部如何实现。
  • 绝不要在测试之间共享可变状态 - 依赖执行顺序或先前测试状态的测试会造成不稳定、不可靠的测试套件。每个测试都必须完全独立,拥有全新的设置。
  • 绝不要在测试文件中使用 any 或跳过类型检查 - 当实现签名发生变化时,使用 as any 的测试会静默通过,同时却用错误的参数调用函数。你会发布 TypeScript 本可以捕获的破损代码。测试是可执行的文档:user as any 什么也传达不了,而 createTestUser(Partial<User>) 则准确说明了对这个测试用例而言哪些属性重要。
  • 绝不要在未运行 TypeScript 类型检查的情况下将测试文件标记为完成 - 测试文件通常被排除在 tsconfig.json 的编译路径之外,因此在项目根目录运行 tsc 不会捕获测试中的类型错误。测试中的类型错误会导致运行时失败、不正确的测试行为,以及来自未测试其所声称内容的测试的虚假信心。在将任何测试文件标记为"完成"之前,你必须使用项目的包管理器(npm/pnpm/bun/yarn)直接对该测试文件运行 tsc --noEmit。对于 monorepo,先 cd 进入具体的包目录,然后再运行类型检查。在继续之前修复所有类型错误——绝不要使用 as any 或 @ts-ignore 来绕过错误。
  • 绝不要假设 TypeScript 类型能防止运行时错误 - TS 类型仅在编译时存在,在运行时消失。只测试"类型有效"的输入会制造虚假的安全感。在生产环境中,函数会从未经校验的 JSON API、JSON.parse() 结果、外部库、用户输入和数据库记录接收无效数据。一个类型标注为 process(data: ValidData) 的函数在运行时仍可能接收到 null、undefined 或格式错误的对象。测试防御性编程场景:向非空参数传递 null,向必填字段传递 undefined,向有类型的参数传递格式错误的对象。这些"类型无效"的测试能捕获 TypeScript 无法防止的真实 bug。
  • 绝不要在存在更强属性时编写弱属性 - 仅验证"未抛出异常"或"返回了一个值"的基于属性的测试提供的覆盖极少。测试 encode/decode 对时,验证往返相等性(decode(encode(x)) === x),而不仅仅是 decode 成功。测试归一化时,验证幂等性(normalize(normalize(x)) === normalize(x)),而不仅仅是它返回一个字符串。弱属性给出虚假信心:它们通过,但并未真正验证正确性。

编写测试前,先思考

在实现测试前,应用以下专家思维模式:

是否应该测试此文件?
  • 此文件是否包含需要测试的行为? 只声明常量、类型或数据结构而不包含逻辑的文件不需要测试。常量文件(export const X = 42)、类型定义文件(type User = {...})、GLSL uniform 声明、配置对象和纯数据文件都没有需要验证的行为。如果文件不包含函数、逻辑或转换,就跳过测试。测试行为,而不是数据。
测试隔离与设置
  • 清理逻辑应该放在哪里? 分层思考:配置消除整个类别的错误(在 vitest.config.ts 中清理 mock),设置文件处理项目级事项(自定义匹配器、全局 mock),beforeEach 处理测试专属状态。每个测试都自行清理 mock,就像每个函数都自行检查 null 一样——虽然可行,却没有抓住要点。将关注点放到适当的最高层级。
  • 此测试是否依赖之前的测试或共享状态? 测试套件是彼此独立的平行宇宙——无论每个测试最先运行、最后运行还是单独运行,它都应该表现一致。状态依赖会产生“量子测试”,其通过或失败取决于执行顺序。如果一个测试需要另一个测试的数据,那么它们实际上是被人为拆分的同一个测试。
测试什么
  • 我测试的是行为还是实现? 测试用户体验到的内容(输入 → 输出),而不是代码如何实现(调用了哪些函数)。实现测试会在安全重构时失效。
  • 我能使用的最简单依赖是什么? 真实实现 > 假实现 > 桩 > 间谍 > 模拟。沿着这个层级逐步往下,每一步都会增加脆弱性。只有在使用真实代码不切实际时(外部 API、耗时操作),才使用模拟。
测试清晰度
  • 别人能在 5 秒内理解此测试吗? 遵循 AAA 模式(Arrange、Act、Assert),明确划分各个阶段。如果设置过程复杂,就提取到名称清晰的辅助函数中。
  • 同一行为是否有多种变体? 使用 it.each() 编写参数化测试,而不是复制测试结构。每个概念只写一个断言,让测试保持专注。
性能与维护
  • 6 个月后,此测试仍然有价值吗? 避免测试框架内部机制或琐碎操作。专注于真正能防止错误的业务逻辑、边界情况和错误处理。
  • 此测试是否足够快,能在每次保存时运行? 避免在测试中执行昂贵操作。对数据库使用假实现,用模拟计时器处理延迟,并为外部调用设置桩。测试应在毫秒内完成。

此技能涵盖的内容

vitest 测试模式专家指南:

  1. 组织 - 文件位置、命名、describe 块结构
  2. AAA 模式 - Arrange、Act、Assert,一目了然
  3. 参数化测试 - 使用 it.each() 减少重复
  4. 错误处理 - 测试异常、边界情况、故障注入
  5. 断言 - 使用严格断言捕获意外值
  6. 测试替身 - 假实现、桩、模拟、间谍的层级及各自的使用场景
  7. 异步测试 - Promises、async/await、计时器、并发测试
  8. 性能 - 通过高效设置和全局配置加快测试
  9. Vitest 功能 - 覆盖率、监视模式、设置文件、配置发现
  10. 快照测试 - 何时快照有助于维护,何时会造成阻碍
  11. 基于属性的测试 - 使用 fast-check 和生成的输入增强覆盖率

使用方法

此技能采用渐进式披露结构,以尽量减少上下文用量:

1. 从概览(AGENTS.md)开始

阅读 AGENTS.md,了解所有规则的简要概览(每条规则均附一行摘要),以及发现现有测试配置的工作流。

2. 检查现有测试配置

编写测试前:

  • 首先检查 vitest.config.ts 中的全局模拟清理设置(clearMocks、mockReset、restoreMocks)
  • 然后搜索设置文件(test/setup.ts、vitest.setup.ts 等),并分析其配置
  • 查看 AGENTS.md 中的工作流
3. 按需加载特定规则

使用以下明确触发条件,判断何时加载每个参考文件:

必须加载(加载整个文件):

遇到以下模式时加载:

除非确有需要,否则不要加载:

4. 应用模式

每个参考文件都包含:

  • ❌ 展示反模式的错误示例
  • ✅ 展示最佳实现的正确示例
  • 关于该模式为何重要的说明
5. 使用报告模板

通过此技能审查测试代码时,请使用标准化报告格式:

模板: assets/output-report-template.md

报告格式包括:

  • 执行摘要及测试质量影响评估
  • 用于确定优先级的严重程度级别(Critical、High、Medium、Low)
  • 影响分析(测试可靠性、可维护性、性能、清晰度)
  • 分类(测试组织、断言、测试替身、异步测试、性能)
  • 指向 references/ 中详细指南的模式引用
  • 用于跟踪所有问题的汇总表

何时使用报告模板:

  • 通过 /accelint-ts-testing <path> 直接调用技能
  • 用户要求跨一个或多个文件“审查测试代码”或“审计测试”,从而隐式调用技能

何时不使用报告模板:

  • 用户要求“为此函数编写一个测试”(直接实现)
  • 用户询问“这个测试有什么问题?”(回答问题)
  • 用户要求修复特定测试(直接应用修复,不生成正式报告)

重要:审计测试时,务必检查基于属性的测试机会

  • 每次审计都要加载 property-based-testing.md
  • 遵循 AGENTS.md 中的“工作流:测试代码审查/审计”
  • 检查高价值的 PBT 模式:编码/解码对、规范化器、验证器、纯函数、排序函数
  • 即使未发现其他问题,也要在审计报告中列出 PBT 机会

快速示例

请参阅 quick-start.md,了解完整的前后对比示例,展示此技能如何将含糊不清的测试改进为清晰、易维护的测试。

评论

支持 Markdown