研发协作
写测试时如何确保测的是行为而非实现
当你编写、修改或保留一个测试时,用它来检查该测试是否真正验证了可观察的行为。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
写测试时如何确保测的是行为而非实现通用
解决思路
- 调用代码时像它的用户那样调用,并针对字面期望值断言用户观察到的结果。
- 保留测试前先自问:如果它导入的每个函数都返回 undefined,这个测试还会通过吗?如果会,说明它没有观察任何行为,无法因缺陷而失败,应重写断言或删除该测试。
- 修复弱断言或无断言:不要只写 toBeDefined、toBeTruthy、not.toThrow、toBeInstanceOf、toBeGreaterThan(0),改为断言具体的字面输出。
- 修复只验证 mock 或不存在的断言:不要只用 toHaveBeenCalled、not.toHaveBeenCalled、toBeUndefined、toEqual([])、toHaveLength(0)、not.toBe(wrongValue),改为断言 mock 收到的 payload 或调用后的状态。
- 修复自引用断言:不要写 expect(f(a)).toBe(f(a)) 或 expect(parsed.url).toBe(buildUrl(...)) 这类期望值来自被测代码本身的断言。
- 修复常量钉死:不要写 expect(LIMITS.maxTools).toBe(8) 或 expect(PROMPT).toContain("You are") 这类复述手工维护常量、配置默认值、表格行或提示词字符串的断言,改为用一个输入测试读取该常量的机制。
- 修复 fixture 断言 fixture:不要在断言中读取测试自己构造的数据或 beforeEach 中计算的值,而让被测对象从未在测试体内运行。
- 在测试体内用一个具体输入调用被测对象,并断言字面输出或可观察效果,例如 expect(slugify("Hello, World!")).toBe("hello-world")。
- 对于「不存在」的断言,在同一测试中用另一个输入断言其「存在」。
- 对于 mock,断言它收到的 payload 或调用后的状态,而不是断言它被调用过。
- 当不存在这样的断言时,删除该测试。
- 保留跨表格行的关系测试(如两个表中都存在的键、存在的父项),以及 *.test-d.ts 文件中的编译期检查。
适用边界 · 注意事项
- 测试跨表格行的关系(如两个表中都存在的键、存在的父项)时,不要套用此原则去删除。
- *.test-d.ts 文件中的编译期检查不适用此原则。