You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
test(repo): prefer test.each over for/forEach loops in tests (#303)
Convert hand-rolled parameterization loops in test bodies (and around
test definitions) to test.each so each case becomes its own reported
test with pinpointed failure attribution. Document the convention in
.claude/rules/testing.md, including Bun's readonly-array spread gotcha
and the runtime-data exception.
Copy file name to clipboardExpand all lines: .claude/rules/testing.md
+30Lines changed: 30 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -26,4 +26,34 @@ This runs each unit and integration test file as a separate `bun test` subproces
26
26
27
27
Prefer `spyOn()` for mocking, and always restore spies in `afterAll` with `mockRestore()`.
28
28
29
+
Never use `for` or `forEach` loops inside a single test to verify multiple inputs or cases — use `test.each` (or `it.each` / `describe.each`) so each case is its own reported test case with its own name, setup/teardown, and pinpointed failure output.
Bun's `test.each` rejects `readonly`/`as const` arrays via its literal-inferring overload. Spread to a mutable copy so the literal union is preserved in the callback type:
46
+
47
+
```ts
48
+
const MODES = ["human", "agent"] asconst;
49
+
test.each([...MODES])("mode %s", (mode) => {
50
+
/* mode: "human" | "agent" */
51
+
});
52
+
```
53
+
54
+
Exceptions where a loop in the test body is fine:
55
+
56
+
- The iteration itself is the behavior under test (asserting an event fires N times, accumulating state across steps).
57
+
- The data being iterated is collected at runtime inside the test and cannot be expressed as a static array at module-load time (e.g. `http.requests` after the action runs, files map captured from a callback).
58
+
29
59
`mock.module()` is acceptable only when registered at file top, before any consumer of the mocked module is loaded (the integration harness at `packages/cli-core/src/test/integration/lib/harness.ts` and `packages/cli-core/src/lib/credential-store.test.ts` both follow this pattern). In Bun 1.x, `mock.module()` registrations are process-lifetime and will pollute the module registry for any later test file that imports the same module via a non-mocked path, so do not call `mock.module()` from inside `beforeEach`/`describe`/`test`, and do not introduce it in test files that will run alongside files importing the real module.
0 commit comments