principle-test-behavior-not-implementation
测行为,不测实现
A test calls the code the way its users do and asserts the result they observe against a literal expected value. A test that asserts which calls the code made, or restates a constant the code contains, does neither.
测试按用户方式调用代码,并用字面期望值断言他们观察到的结果。断言代码做了哪些调用,或复述代码里某个常量的测试,两样都没做。
The check: before you keep a test, ask whether it would still pass if every function it imports returned undefined. If yes, it observes no behavior and cannot fail for a defect. Rewrite the assertion or delete the test.
自检:保留测试前,问若它 import 的每个函数都返回 undefined,测试是否仍过。是,就观察不到行为,也不会因缺陷失败。改断言或删测试。
Why: A test that cannot fail for a defect costs CI time and review attention and catches nothing. A constant pin also fails when someone edits the constant or the prompt it restates, so it prevents that edit.
为什么: 不能因缺陷失败的测试白耗 CI 时间和审阅注意力,什么也抓不到。钉死常量也会在有人改常量或它复述的 prompt 时失败,从而阻止那次编辑。
Five shapes that still pass when every imported function returns undefined:
在每个 import 函数返回 undefined 时仍会过的五种形状:
-
Weak or no assertion. No
expect, or onlytoBeDefined,toBeTruthy,not.toThrow,toBeInstanceOf,toBeGreaterThan(0). -
Mock or absence only. Only
toHaveBeenCalled,not.toHaveBeenCalled,toBeUndefined,toEqual([]),toHaveLength(0),not.toBe(wrongValue). -
Self-referential. The expected value comes from the code under test:
expect(f(a)).toBe(f(a)),expect(parsed.url).toBe(buildUrl(...)). -
Constant pin. The assertion restates a hand-maintained constant, config default, table row, or prompt string:
expect(LIMITS.maxTools).toBe(8),expect(PROMPT).toContain("You are"). -
Fixture asserts fixture. The assertion reads data the test built or a value computed in
beforeEach, and the subject never runs inside the body. -
弱断言或无断言。 没有
expect,或只有toBeDefined、toBeTruthy、not.toThrow、toBeInstanceOf、toBeGreaterThan(0)。 -
只测 mock 或缺失。 只有
toHaveBeenCalled、not.toHaveBeenCalled、toBeUndefined、toEqual([])、toHaveLength(0)、not.toBe(wrongValue)。 -
自指。 期望值来自被测代码:
expect(f(a)).toBe(f(a))、expect(parsed.url).toBe(buildUrl(...))。 -
钉死常量。 断言复述手维护常量、配置默认值、表行或 prompt 字符串:
expect(LIMITS.maxTools).toBe(8)、expect(PROMPT).toContain("You are")。 -
fixture 断言 fixture。 断言读的是测试自己建的数据或
beforeEach算出的值,被测主体在 body 里根本没跑。
The fix: call the subject inside the test body with one concrete input and assert the literal output or the observable effect, expect(slugify("Hello, World!")).toBe("hello-world"). For an absence, assert the presence on the other input in the same test. For a constant, test the mechanism that reads it with one input instead of restating the value. For a mock, assert the payload it received or the state after the call, not that it was called. When no such assertion exists, delete the test.
修法: 在测试 body 里用一个具体输入调用主体,断言字面输出或可观察效果,如 expect(slugify("Hello, World!")).toBe("hello-world")。测缺失时,同测里断言另一输入上的存在。测常量时,测读取它的机制加一个输入,别复述值。测 mock 时,断言它收到的载荷或调用后状态,别断言「被调用过」。没有这种断言就删测试。
Keep a test of a relation across a table’s rows (a key present in two tables, a parent that exists), and a compile-time check in a *.test-d.ts file.
保留跨表行关系的测试(两表都有的 key、存在的 parent),以及 *.test-d.ts 里的编译期检查。