mcpField
T
testing

testing

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

关于 testing

这项测试技能整理了 TypeScript 测试的组织、断言命名及测试与基准测试的区分规范,也提供类型测试、测试设置、审查和精简测试的指引。

主要功能

  1. 01Result 断言辅助工具
  2. 02区分测试与基准测试
  3. 03测试文件文档规范
  4. 04按行为拆分测试文件
  5. 05基于行为的测试命名

适用场景

  1. 01为 wellcrafted Result 编写测试
  2. 02将性能报告与 CI 测试分开
  3. 03按不同测试行为拆分文件
  4. 04审查或精简现有测试

安装

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

$npx skills add https://github.com/epicenterhq/epicenter --skill testing

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

包含的文件

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

references/
honest-tests.md
setup-pattern.md
test-deletion-grill.md
test-structure.md
type-testing.md

详细介绍

测试文件约定

参考资料

根据当前工作内容按需查阅:

  • 如果正在处理负向类型测试(@ts-expect-error、bun:test 类型测试策略、不使用 as any),请阅读 references/type-testing.md
  • 如果正在处理测试设置架构(setup() 模式、可组合的设置、避免使用 beforeEach、共享 schema),请阅读 references/setup-pattern.md
  • 如果正在处理测试组织结构(扁平测试、describe() 边界、优先使用辅助函数而非嵌套),请阅读 references/test-structure.md
  • 如果正在审查现有测试,检查是否存在模棱两可的断言、透传 getter、停滞的 fake、无用的 fake 接口,或与代码相矛盾的文档字符串,请阅读 references/honest-tests.md
  • 如果正在删除或精简测试,而这些测试可能没有保留价值,请阅读 references/test-deletion-grill.md

外部阅读资料:

相关技能:请参阅 services-layer,了解正在测试的服务模式。请参阅 typescript,了解类型测试约定。

结果断言

当测试断言 wellcrafted Result 时,请使用 wellcrafted/testing 中的 expectOk 和 expectErr,而不要手动编写错误检查。

import { expectErr, expectOk } from 'wellcrafted/testing';

const data = expectOk(await service.doThing());
expect(data.id).toBe('1');

const error = expectErr(await service.doThing({ invalid: true }));
expect(error.name).toBe('InvalidInput');

当值是 wellcrafted Result 时,避免克隆本地辅助函数、使用 expect(error).toBeNull() 检查成功,以及通过 if (error) throw ... 解包。

此规则不适用于普通响应正文、UI 快照或其他仅包含 error 属性的对象。

测试与基准测试

两种不同的文件扩展名,对应两种不同的用途:

  • *.test.ts:使用 expect() 断言行为。在 bun test 下运行(仓库默认配置,CI)。没有至少一次 expect() 调用的测试文件不应使用此扩展名。
  • *.bench.ts:测量并报告。输出表格、耗时或存储大小。仅在 bun bench 下运行。无需断言(共享硬件上的性能阈值容易波动;建议关注可视化趋势)。

单个文件只能属于其中一种,不能兼具两者。基准测试位于包内的 src/__benchmarks__/ 下;测试则与所覆盖的模块放在一起。bun test 的默认发现 glob 只会匹配 *.test.ts 及同类文件,因此将报告文件从 .test.ts 重命名为 .bench.ts,即可将其排除在 CI 之外。

文件级文档注释

每个 .test.ts 文件都必须以一个 JSDoc 块开头,说明测试内容以及验证的关键行为。这是对模块契约的说明。

结构
/**
 * [Module Name] Tests
 *
 * [1-3 sentences explaining what this file tests and why these tests matter.]
 *
 * Key behaviors:
 * - [Behavior 1]
 * - [Behavior 2]
 * - [Behavior 3]
 *
 * See also:
 * - `related-file.test.ts` for [related aspect]
 */
良好示例
/**
 * Cell-Level LWW CRDT Sync Tests
 *
 * Verifies cell-level LWW conflict resolution where each field
 * has its own timestamp. Unlike row-level LWW, concurrent edits to
 * DIFFERENT fields merge independently.
 *
 * Key behaviors:
 * - Concurrent edits to SAME field: latest timestamp wins
 * - Concurrent edits to DIFFERENT fields: BOTH preserved (merge)
 * - Delete removes all cells for a row
 */
不良示例(过于简略)
// Tests for create-tables
章节标题

对于较长的测试文件(100 行以上),请使用注释标题分隔逻辑章节:

// ============================================================================
// MESSAGE_SYNC Tests
// ============================================================================

按多个方面拆分测试文件

如果一个模块包含多个不同的行为方面,请拆分为多个专注的测试文件,而不要全部放在一个庞大的文件中:

模式使用场景
{module}.test.ts核心 CRUD 行为、正常路径、边缘情况
{module}.types.test.ts类型推断验证、负向类型测试
{module}.{scenario}.test.ts特定场景(CRDT 同步、离线、集成)
何时拆分
  • 文件超过约 500 行
  • 测试涵盖真正不同的关注点(CRUD 与同步、类型)
  • 不同关注点需要不同的设置
何时不拆分
  • 拆分后会产生少于 3 个测试的文件
  • 所有测试共用相同的设置并关注同一事项

测试命名

测试描述必须是行为断言,而不是含糊的描述。测试失败时,名称应能说明出错之处。

规则
  1. 描述发生了什么,不要写“应该正常工作”或“处理正确”
  2. 测试边界情况时,包含相应条件
  3. 不要使用填充词:“应该”“正确地”“恰当地”都没有意义
好的名称
test('upsert stores row and get retrieves it', () => { ... });
test('filter returns only published posts', () => { ... });
test('concurrent edits to different fields: both preserved', () => { ... });
test('delete vs update race: update wins (rightmost entry)', () => { ... });
test('observer fires once per transaction, not per operation', () => { ... });
test('get() throws for undefined tables with helpful message', () => { ... });
不好的名称
test('should work correctly', () => { ... });         // What works? What's correct?
test('should handle batch operations', () => { ... }); // Handle how?
test('basic test', () => { ... });                     // Says nothing
test('should create and retrieve rows correctly', () => { ... }); // Vague "correctly"
模式:{action} {outcome} [condition]
"upsert stores row and get retrieves it"
 ^^^^^^ ^^^^^^^^^^   ^^^ ^^^^^^^^^^^^^
 action  outcome    action  outcome

"observer fires once per transaction, not per operation"
 ^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 subject   outcome              condition

"get() returns not_found for non-existent rows"
 ^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
 action     outcome            condition

评论

支持 Markdown