@kensio/isolated-testing-style 1.8.0 → 1.10.0
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://anthropic.com/claude-code/plugin.schema.json",
|
|
3
3
|
"name": "isolated-testing-style",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.10.0",
|
|
5
5
|
"description": "An opinionated testing style built on simulated collaborators and randomised data.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Kensio Software",
|
package/README.md
CHANGED
|
@@ -28,14 +28,13 @@ simulator asserts that it called the service correctly. A stub answers whatever
|
|
|
28
28
|
answer. It agrees with your understanding of the API by construction, and cannot find the case where
|
|
29
29
|
that understanding is wrong.
|
|
30
30
|
|
|
31
|
-
**
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
test, and it is faster.
|
|
31
|
+
**Take isolation from randomised data.** Randomised values from faker mean two tests cannot collide.
|
|
32
|
+
There is nothing to tear down and no ordering to depend on. A shared environment built the way
|
|
33
|
+
production is built is closer to production than a minimal one rebuilt per test, and it is faster.
|
|
35
34
|
|
|
36
|
-
**Assert behaviour
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
**Assert observable behaviour.** To prove a value is cached, delete the underlying resource and show
|
|
36
|
+
the cached value survives. To prove a retry, make the first call fail and the second succeed. Both
|
|
37
|
+
hold however the code is implemented. That is what makes refactoring safe.
|
|
39
38
|
|
|
40
39
|
**Never pin a value computed by the code under test.** Pinning a hash you generated by running the
|
|
41
40
|
same function only proves the function is deterministic. It keeps passing after the function becomes
|
|
@@ -44,8 +43,8 @@ wrong. Pin against an independent authority, or let a real implementation valida
|
|
|
44
43
|
**Put test support beside the code.** Factories and helpers live in a test-support module next to
|
|
45
44
|
what they support. Test files hold tests.
|
|
46
45
|
|
|
47
|
-
**Comment test bodies with Given, When and Then**, saying why the lines are there
|
|
48
|
-
|
|
46
|
+
**Comment test bodies with Given, When and Then**, saying why the lines are there. A comment that
|
|
47
|
+
restates the code adds nothing.
|
|
49
48
|
|
|
50
49
|
## The other two skills
|
|
51
50
|
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@ description: Write tests that start from given/when/then, use real collaborators
|
|
|
6
6
|
# Isolated testing style
|
|
7
7
|
|
|
8
8
|
An opinionated way of writing tests. The examples are TypeScript and vitest, but the rules are about
|
|
9
|
-
test design
|
|
9
|
+
test design and hold for any framework. Each rule exists because of a specific failure it would have
|
|
10
10
|
caught.
|
|
11
11
|
|
|
12
12
|
## Start with Given, When, Then
|
|
@@ -28,14 +28,14 @@ what should result. That is a smaller question than "how do I test this?", and y
|
|
|
28
28
|
answer it before you can answer the bigger one.
|
|
29
29
|
|
|
30
30
|
**It designs the interface.** Filling in `// When` forces you to name the single action under test,
|
|
31
|
-
in the caller's vocabulary
|
|
32
|
-
|
|
31
|
+
in the caller's vocabulary. A step you cannot write as one `// When` usually means the interface is
|
|
32
|
+
wrong.
|
|
33
33
|
|
|
34
34
|
**It keeps the test readable as documentation.** Tests are read far more often than they are
|
|
35
35
|
written, and without the structure it is easy to produce a body where essential behaviour and
|
|
36
36
|
incidental setup look alike.
|
|
37
37
|
|
|
38
|
-
Then fill each comment in with the case,
|
|
38
|
+
Then fill each comment in with the case it covers, and never with a restatement of the code:
|
|
39
39
|
|
|
40
40
|
```typescript
|
|
41
41
|
it("refuses an order once the offer has closed", async () => {
|
|
@@ -96,11 +96,11 @@ quirk to work around.
|
|
|
96
96
|
|
|
97
97
|
## Keep setup cheap and independent
|
|
98
98
|
|
|
99
|
-
Tangled shared fixtures come from economics
|
|
100
|
-
proportion to how expensive it is to build. When getting a test into the right state
|
|
101
|
-
through a web of existing fixtures, reusing what is already there is the rational
|
|
102
|
-
reuse adds another edge to the graph. That is how a suite arrives at setup that no
|
|
103
|
-
touch.
|
|
99
|
+
Tangled shared fixtures come from economics. Discipline has little to do with it. Teams share setup
|
|
100
|
+
roughly in proportion to how expensive it is to build. When getting a test into the right state
|
|
101
|
+
means threading through a web of existing fixtures, reusing what is already there is the rational
|
|
102
|
+
move, and each reuse adds another edge to the graph. That is how a suite arrives at setup that no
|
|
103
|
+
one dares to touch.
|
|
104
104
|
|
|
105
105
|
The fix is to share differently. Shared factories for test entities are exactly what you want. A
|
|
106
106
|
factory that constructs a type is worth writing once and using everywhere. What has to be avoided is
|
|
@@ -109,7 +109,7 @@ those factories getting tangled up with each other. Each piece of setup should s
|
|
|
109
109
|
Independence comes from taking dependencies explicitly rather than reaching for ambient state. A
|
|
110
110
|
factory that is handed what it needs stays pure, and the test decides what to hand it.
|
|
111
111
|
`@kensio/part-factory` builds this in. Factories take a `dependencies` object as a second argument
|
|
112
|
-
at call time. A factory that needs a simulated AWS is
|
|
112
|
+
at call time. A factory that needs a simulated AWS is handed one, and never goes looking.
|
|
113
113
|
|
|
114
114
|
```typescript
|
|
115
115
|
// Given an order that exists in this test's own simulated AWS.
|
|
@@ -121,12 +121,12 @@ A factory built that way can be shared as widely as you like and still stand on
|
|
|
121
121
|
everything it does is independent of what another factory did first. Prefer collaborators and
|
|
122
122
|
factories that need only instantiation, with no side effects, no cleanup and no coordination.
|
|
123
123
|
|
|
124
|
-
For a step specific to one test
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
For a step specific to one test, ask whether the step belongs to the test or the test belongs to the
|
|
125
|
+
step. A helper confined to one file can be pulled back inline later if it stops earning its place,
|
|
126
|
+
whereas a fixture that a large part of the suite is built on cannot. Reversibility is the thing to
|
|
127
|
+
preserve, and locality on its own earns little.
|
|
128
128
|
|
|
129
|
-
##
|
|
129
|
+
## Take isolation from randomised data
|
|
130
130
|
|
|
131
131
|
Randomised values make collisions impossible. No teardown is required, and there is no ordering to
|
|
132
132
|
depend on. Randomised is enough. Guaranteed uniqueness is unnecessary, and a UUID has no realistic
|
|
@@ -174,7 +174,7 @@ Faker is the source of these values. Prefer a generator that produces a realisti
|
|
|
174
174
|
kind (`faker.internet.email()`, `faker.string.uuid()`, `faker.company.name()`) over a counter or a
|
|
175
175
|
literal with a suffix. The test data also exercises the shapes production data has.
|
|
176
176
|
|
|
177
|
-
## Assert behaviour
|
|
177
|
+
## Assert observable behaviour
|
|
178
178
|
|
|
179
179
|
A call count asserts how the code is written today. A behaviour assertion holds however it is
|
|
180
180
|
written, which is what lets you refactor.
|
|
@@ -228,9 +228,9 @@ Two ways out, in order of preference:
|
|
|
228
228
|
published test vector, or a value produced by a different implementation.
|
|
229
229
|
|
|
230
230
|
The same rule covers snapshot tests of anything the code under test formats. A snapshot records what
|
|
231
|
-
the code does,
|
|
231
|
+
the code does, and stays silent on what it should do.
|
|
232
232
|
|
|
233
|
-
##
|
|
233
|
+
## Keep the top level to imports
|
|
234
234
|
|
|
235
235
|
In an ideal vitest or jest file, the only things outside the top-level `describe()` are the imports.
|
|
236
236
|
State, construction and helpers all live inside it. The file reads as a description of behaviour and
|
|
@@ -248,14 +248,14 @@ describe("placing an order", () => {
|
|
|
248
248
|
});
|
|
249
249
|
```
|
|
250
250
|
|
|
251
|
-
This is mostly a consequence of the other rules
|
|
252
|
-
top level of a test file is module-level state the tests share, a mutable handle that exists
|
|
253
|
-
`afterEach` can reach it, and hoisted mock registrations. The rules above have already turned
|
|
254
|
-
all three. So a top level that will not stay empty is a useful signal that something further up
|
|
255
|
-
slipped. Imported factories are fine here. They arrive as imports precisely because they stand
|
|
256
|
-
their own.
|
|
251
|
+
This is mostly a consequence of the other rules and adds little on its own. What usually accumulates
|
|
252
|
+
at the top level of a test file is module-level state the tests share, a mutable handle that exists
|
|
253
|
+
so `afterEach` can reach it, and hoisted mock registrations. The rules above have already turned
|
|
254
|
+
down all three. So a top level that will not stay empty is a useful signal that something further up
|
|
255
|
+
has slipped. Imported factories are fine here. They arrive as imports precisely because they stand
|
|
256
|
+
on their own.
|
|
257
257
|
|
|
258
|
-
## Put test support beside the code
|
|
258
|
+
## Put test support beside the code
|
|
259
259
|
|
|
260
260
|
A test file should hold tests. Where support lives depends on what it is for.
|
|
261
261
|
|
|
@@ -275,8 +275,9 @@ A step written for **one test** stays in that test's file, inside the `describe`
|
|
|
275
275
|
when another test wants it is fine. Make it independent first, so what spreads is a self-contained
|
|
276
276
|
factory (not a dependency on how some other test left things).
|
|
277
277
|
|
|
278
|
-
If a test file is mostly setup, that is a signal. The fix is usually cheaper construction
|
|
279
|
-
|
|
278
|
+
If a test file is mostly setup, that is a signal. The fix is usually cheaper construction. A shared
|
|
279
|
+
fixture treats the symptom. Scroll the file and see how much of it is `it(...)` bodies making
|
|
280
|
+
assertions.
|
|
280
281
|
|
|
281
282
|
## Tools that help
|
|
282
283
|
|