@boopkit/cap-testing 0.5.9 → 0.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -46,6 +46,27 @@ expect(item.state).toBe('submitted'); // the effect
|
|
|
46
46
|
|
|
47
47
|
---
|
|
48
48
|
|
|
49
|
+
## Test a promise the design makes, not what the code emits
|
|
50
|
+
|
|
51
|
+
**A test earns its keep by checking a promise the design makes, and the difference lies in where the expected value comes from.**
|
|
52
|
+
Derive it from the design — a rule in the spec, an invariant, a grammar, a stated guarantee — and the test fails the moment the code breaks the promise.
|
|
53
|
+
Copy it from what the code produces, and the test only confirms that the code agrees with itself: it stays green through a bug, and certifies the bug as correct.
|
|
54
|
+
|
|
55
|
+
This is the layer beneath "Assert the effect, not the envelope".
|
|
56
|
+
Asserting a real state change is necessary, but if the expected state was read off the running implementation, the test still mirrors the code.
|
|
57
|
+
|
|
58
|
+
Ask of every assertion whether it would pass on a plausible wrong implementation.
|
|
59
|
+
A test you cannot picture failing validates nothing.
|
|
60
|
+
An expectation drifts onto the implementation in two common ways:
|
|
61
|
+
|
|
62
|
+
- **The mirror.** The expected value is whatever the code printed, pasted into the assertion, so the test locks in current behaviour, bugs included. Compute the expected value independently, from the inputs and the design's rule, rather than reading it back from the unit under test.
|
|
63
|
+
- **The single producer.** A promise that must hold everywhere ("every record carries a valid owner", "each key maps to exactly one handler") is asserted only on the path you happened to be working on, so another producer of the same promise, overlooked or added later, breaks it silently. Make the promise the subject of the test, and cover every producer of it.
|
|
64
|
+
|
|
65
|
+
Asserting the effect, round-trip fidelity, data-driven config coverage and schema contract walks are all ways to pin a design's promise to real behaviour.
|
|
66
|
+
Pointed at the implementation's output instead, each one becomes a mirror that passes forever and proves nothing.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
49
70
|
## Quarantine convention — a skipped test must cite a tracking ticket
|
|
50
71
|
|
|
51
72
|
**"Known failing" must be a bounded, tracked, VISIBLE state — never ambient noise.** A suite that runs chronically red carries zero signal: a genuine new failure hides among the tolerated ones. Quarantine-with-a-ticket converts "N flaky failures" into "N tracked exceptions", so any *unexpected* red is a real signal again.
|
|
@@ -65,7 +86,7 @@ A skip carrying neither a ticket reference nor a `SKIP-OK:` reason is a conventi
|
|
|
65
86
|
|
|
66
87
|
## Which tests to run, and when
|
|
67
88
|
|
|
68
|
-
**The rule.** A change runs the focused tests for what it touched, plus the project's whole-program static check. The full suite runs once per branch, on the work that completes it. The `boop-project-mgmt-beads-planning` skill carries the canonical wording to put on issues; this section is the reasoning behind it, and the two have to keep agreeing.
|
|
89
|
+
**The rule.** A change runs the focused tests for what it touched, plus the project's whole-program static check. A change that reaches further than its own tests widens that run deliberately, as described below. The full suite runs once per branch, on the work that completes it. The `boop-project-mgmt-beads-planning` skill carries the canonical wording to put on issues; this section is the reasoning behind it, and the two have to keep agreeing.
|
|
69
90
|
|
|
70
91
|
**The commands are `npm test` for the suite and `npm run typecheck` for the static check.** The project's stack capability supplies both as npm scripts — `typescript` in a TypeScript project — so every project has the same two names, and the static check has a name rather than an invocation each agent has to derive. A focused run names the test files or folders it runs; the stack's testing skill gives its exact form (`boop-typescript-testing` in a TypeScript project). Write that form on an issue, rather than leaving the claiming agent to derive it.
|
|
71
92
|
|
|
@@ -76,6 +97,20 @@ If a project has no `typecheck` script, add one before following the rule, and c
|
|
|
76
97
|
Second, it is slower than the focused run, though see the caution below about how much weight that carries.
|
|
77
98
|
|
|
78
99
|
Third, a suite already red for an unrelated reason gives you nothing to compare against, so mid-change runs on a red tree get retried and re-read rather than acted on.
|
|
100
|
+
A red suite is also slower than a green one.
|
|
101
|
+
A test that fails by waiting for something that never happens runs until its timeout rather than returning, and a runner configured to retry failures runs it again, so the full suite takes longest at the moment you most want its answer.
|
|
102
|
+
Learn what your own suite costs in both states.
|
|
103
|
+
|
|
104
|
+
**Widen a run deliberately, and say so.**
|
|
105
|
+
The static check sees every caller that reaches a change through a type.
|
|
106
|
+
It does not see a change that reaches further by another route: the data layer (a schema, a migration, a hand-written query), the test infrastructure itself (the runner's config, a shared helper, a fixture or setup file), or a file other code reads at run time (a config table, a template).
|
|
107
|
+
For a change like that, whoever writes the issue widens its run scope to the tests that exercise what the change reaches — a folder, a subsystem's tests, or the whole suite when the test infrastructure changed — and says on the issue what to run and why.
|
|
108
|
+
Those are examples; the outcome to aim for is a run that would go red if the change broke something it reaches.
|
|
109
|
+
|
|
110
|
+
The claiming agent runs the scope its issue names, no narrower and no wider, and its closing comment names what it ran.
|
|
111
|
+
If the work turns out to reach further than that scope, the agent says so in its closing comment, naming the tests it expects would catch the break, rather than widening the run on its own judgement.
|
|
112
|
+
The branch's full-suite run still catches what the narrow run missed, and the note tells the reviewer where to look.
|
|
113
|
+
A reflexive widening and a considered one look the same in a log; the stated reason is what tells them apart.
|
|
79
114
|
|
|
80
115
|
**Do not rest the rule on the suite being slow.** A suite's wall-clock time is a property of how the runner is invoked as much as of the tests themselves, and it can fall by an order of magnitude on a single configuration change — running the files in a recyclable worker pool rather than in one long-lived process is the common case. A rule argued from "the suite takes tens of minutes" quietly expires the day somebody fixes the runner, and then it reads as superstition. Argue it from instrument fit, which does not expire.
|
|
81
116
|
|
|
@@ -89,6 +124,13 @@ Those settings are not decoration, and the one that matters is not always the on
|
|
|
89
124
|
|
|
90
125
|
Keep those settings in the runner's config file rather than in the `test` script, so that the focused runs this section asks for inherit them too. The stack capability scaffolds them that way.
|
|
91
126
|
|
|
127
|
+
**Before trusting a narrowed run that went through an npm script, check that the script is a single runner command.**
|
|
128
|
+
npm appends everything after `--` to the end of the script's command string.
|
|
129
|
+
While the script is one runner invocation, `npm test -- <path>` and a name filter narrow the run just as they would on the runner's own command line.
|
|
130
|
+
If the script chains commands, as in `a && b`, the arguments reach only the last command, which is often not the runner at all.
|
|
131
|
+
The runner then runs everything, and a pass looks like the narrowed run you asked for.
|
|
132
|
+
Read the script in `package.json` first; if it chains, use the stack's focused-run form, which calls the runner directly and still reads its config file.
|
|
133
|
+
|
|
92
134
|
**A worker pool makes test isolation a requirement, not a nicety.** More than one worker means test *files* run at the same time, in separate processes, against the same backing services. Anything two files share and do not namespace — a table one of them truncates in setup, a fixture directory, a well-known key or account id — is now a race, and it surfaces as a suite that passes alone and fails in a full run. That is the same symptom the shared-dev-database section below describes, reached by a different route, and it invites the same wrong fix of weakening the assertion that caught it. Namespace what each test creates. Where a suite is not isolation-safe yet, run one file at a time until it is, keeping whatever setting bounds each worker's memory.
|
|
93
135
|
|
|
94
136
|
## Integration tests never touch the shared dev database
|