@codefast/di-testing 0.1.3 → 0.2.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.
- package/CHANGELOG.md +78 -0
- package/README.md +46 -22
- package/dist/discovery/dependency-scanner.js +1 -1
- package/dist/discovery/mock-binder.d.ts +1 -1
- package/dist/discovery/mock-binder.js +2 -2
- package/dist/errors/errors.d.ts +12 -0
- package/dist/errors/errors.js +14 -0
- package/dist/index.d.ts +15 -15
- package/dist/index.js +5 -5
- package/dist/mocking/auto-mock.d.ts +2 -2
- package/dist/mocking/mock-factory.d.ts +1 -1
- package/dist/mocking/mock-factory.js +1 -1
- package/dist/test-bed/bed-builder.d.ts +10 -10
- package/dist/test-bed/bed-builder.js +3 -5
- package/dist/test-bed/sociable-builder.d.ts +6 -6
- package/dist/test-bed/sociable-builder.js +5 -5
- package/dist/test-bed/solitary-builder.d.ts +6 -6
- package/dist/test-bed/solitary-builder.js +4 -4
- package/dist/test-bed/test-bed.d.ts +28 -18
- package/dist/test-bed/test-bed.js +32 -14
- package/dist/test-bed/unit-test-bed.d.ts +4 -4
- package/dist/test-bed/unit-test-bed.js +4 -4
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,83 @@
|
|
|
1
1
|
# @codefast/di-testing
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#952](https://github.com/codefastlabs/codefast/pull/952) A suite now states its mock backend once, and a test bed can no longer be typed against one backend and built from
|
|
8
|
+
another. `createTestBed({ mockFactory, metadataReader? })` returns the entry point every bed begins from, its `Backend`
|
|
9
|
+
inferred from the factory; `TestBed.solitary(target)` and `TestBed.sociable(target)` take the target only. The `TestBed`
|
|
10
|
+
export is `createTestBed({ mockFactory: defaultMockFactory })`, the built-in spy with no framework needed.
|
|
11
|
+
|
|
12
|
+
Before, the backend was chosen per call — `TestBed.solitary(Unit, { mockFactory: () => vi.fn() })` — and
|
|
13
|
+
`TestBed.solitary<Unit, SinonStub>(Unit)` with no factory type-checked, typed every mock as `SinonStub`, and built the
|
|
14
|
+
default spy. Migrate by creating the suite's entry point once:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { createTestBed } from "@codefast/di-testing";
|
|
18
|
+
import { vi } from "vitest";
|
|
19
|
+
|
|
20
|
+
export const TestBed = createTestBed({ mockFactory: () => vi.fn() });
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`TestBedOptions<Backend>` configures `createTestBed` and requires `mockFactory`; `TestBedStatic<Backend>` names the
|
|
24
|
+
entry point's backend and has no default. `createTestBed` throws the new `MissingMockFactoryError`
|
|
25
|
+
(`MISSING_MOCK_FACTORY`) for a caller past the types who passes no factory, instead of standing the built-in spy in for
|
|
26
|
+
a backend nobody chose.
|
|
27
|
+
|
|
28
|
+
- [#976](https://github.com/codefastlabs/codefast/pull/976) `engines.node` is now `>=24.0.0`, up from `>=22.12.0`, and Node 22 is no longer supported. Node 24.0.0 is the first
|
|
29
|
+
release with explicit resource management built in (`using`, `await using`, `DisposableStack`, `AsyncDisposableStack`,
|
|
30
|
+
`SuppressedError`) and all of ES2025, so the packages use both as the platform ships them instead of shimming them for
|
|
31
|
+
an older line, and the CI matrix runs the unit suite on 24.0.0 itself. Move to Node 24, or stay on the current minor
|
|
32
|
+
while a deployment still runs Node 22.
|
|
33
|
+
|
|
34
|
+
### Patch Changes
|
|
35
|
+
|
|
36
|
+
- [#976](https://github.com/codefastlabs/codefast/pull/976) The README states what a program needs for the declarations' disposal members: the explicit resource management types,
|
|
37
|
+
which no numbered `lib` declares before ES2027. `@types/node` 24 or later loads them, and so does `ESNext.Disposable` in
|
|
38
|
+
`lib`. Without either, TypeScript 7 fails inside `container.d.ts` with TS2550 under `skipLibCheck: false`, and at
|
|
39
|
+
`await using` with TS2318. `@codefast/di-testing` drops a `/// <reference lib="esnext.disposable" />` that TypeScript 7
|
|
40
|
+
stripped from its emitted declarations anyway, and takes the lib from its `tsconfig.json` instead.
|
|
41
|
+
|
|
42
|
+
- [#956](https://github.com/codefastlabs/codefast/pull/956) `README.md` no longer quotes the `@codefast/di` peer range, which had fallen behind `package.json` — the release tooling
|
|
43
|
+
moves that range, so the manifest is the one place it is stated.
|
|
44
|
+
|
|
45
|
+
- [#965](https://github.com/codefastlabs/codefast/pull/965) `README.md` now states TypeScript 7 or later as the floor for the package's types — the one compiler every `@codefast/*`
|
|
46
|
+
package is built and checked with. No code or declaration changed.
|
|
47
|
+
- Updated dependencies:
|
|
48
|
+
- @codefast/di@0.11.0
|
|
49
|
+
|
|
50
|
+
## 0.1.4
|
|
51
|
+
|
|
52
|
+
### Patch Changes
|
|
53
|
+
|
|
54
|
+
- [#892](https://github.com/codefastlabs/codefast/pull/892) [`4c97c5b`](https://github.com/codefastlabs/codefast/commit/4c97c5b8c2314f25ee9df7eac5a4b5dd723d6d90) Thanks [@thevuong](https://github.com/thevuong)! - Lower the monorepo's Node floor from 24 to 22.12, so the packages install and run on the active Node 22 LTS line.
|
|
55
|
+
|
|
56
|
+
`engines.node` becomes `>=22.12.0` across every package — the floor the shared toolchain (oxlint, Vite, Vitest, TanStack
|
|
57
|
+
Start) already requires. Development stays on the latest Node (`.node-version`) for speed, and a CI matrix exercises the
|
|
58
|
+
floor and the active LTS directly, so the floor is a contract CI proves rather than one everyone has to run.
|
|
59
|
+
`@types/node` is pinned to the floor's major (`^22`), with a workspace override holding the whole tree there so a dev
|
|
60
|
+
tool's `@types/node: "*"` peer can no longer pull a newer major and mask an API the floor lacks. The floor stays
|
|
61
|
+
mechanical, not advisory: `@codefast/di` keeps its own `Map` upsert helpers rather than the ES2025
|
|
62
|
+
`Map.prototype.getOrInsert` (which would raise the floor to 26) and its `lib` stays `ES2024`. `@codefast/cli`'s mirror
|
|
63
|
+
step now calls the `node:path` functions directly instead of aliasing them, which the floor's types correctly flag as
|
|
64
|
+
unbound methods.
|
|
65
|
+
|
|
66
|
+
The shared `@codefast/typescript-config` presets pin `lib` and `target` to `ES2024` (was `ESNext`) so the compiler's
|
|
67
|
+
ECMAScript surface matches the Node floor: an ES2025 builtin such as `Map.prototype.getOrInsert` now fails to type-check
|
|
68
|
+
rather than compiling and crashing on Node 22.12. `@codefast/di` and `@codefast/di-testing` already pinned `lib` and are
|
|
69
|
+
unchanged.
|
|
70
|
+
|
|
71
|
+
Internal subpath imports move from a `#/` prefix to a bare `#` (`#core/token`, not `#/core/token`), and the
|
|
72
|
+
`package.json#imports` keys become `#*`/`#tests/*`/`#examples/*` to match. Node's native ESM resolver rejects a
|
|
73
|
+
`#/`-prefixed specifier with `ERR_INVALID_MODULE_SPECIFIER` on the whole Node 22 line (and on Node 24 before 24.14), and
|
|
74
|
+
each package ships those specifiers verbatim inside its published `dist/*.js` for a consumer's Node to resolve — so this
|
|
75
|
+
rename is what actually lets the packages import on the new floor. Purely internal: a consumer's own import paths are
|
|
76
|
+
unchanged.
|
|
77
|
+
|
|
78
|
+
- Updated dependencies [[`4c97c5b`](https://github.com/codefastlabs/codefast/commit/4c97c5b8c2314f25ee9df7eac5a4b5dd723d6d90)]:
|
|
79
|
+
- @codefast/di@0.10.1
|
|
80
|
+
|
|
3
81
|
## 0.1.3
|
|
4
82
|
|
|
5
83
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -19,11 +19,13 @@ production — and you assert against the mocks the bed created.
|
|
|
19
19
|
and builds a mock for each — no per-collaborator `bind(...).toConstantValue(...)`.
|
|
20
20
|
- **Real instance, real wiring.** The unit is constructed through a container, so `@postConstruct`, accessor injection,
|
|
21
21
|
and `@preDestroy` run exactly as in production.
|
|
22
|
-
- **Zero test-framework dependency.** The default
|
|
23
|
-
`() =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
- **Zero test-framework dependency.** The default `TestBed` mocks with a small built-in spy. Create your suite's own
|
|
23
|
+
with `createTestBed({ mockFactory: () => vi.fn() })` — or `jest.fn`, `() => sinon.stub()` — to build every mock from
|
|
24
|
+
that backend and use its matchers instead.
|
|
25
|
+
- **Backend-typed lookups.** The factory's return type flows through every bed the entry point begins: with
|
|
26
|
+
`() => vi.fn()`, `mocks.get(EmailToken).send` carries Vitest's own mock surface (`mockReturnValueOnce`, `mockClear`,
|
|
27
|
+
and so on), with no adapter package and no module augmentation. A bed can have no other backend than its entry point,
|
|
28
|
+
so a mock is never typed as one backend and built from another.
|
|
27
29
|
|
|
28
30
|
## Installation
|
|
29
31
|
|
|
@@ -31,17 +33,21 @@ production — and you assert against the mocks the bed created.
|
|
|
31
33
|
pnpm add -D @codefast/di-testing
|
|
32
34
|
```
|
|
33
35
|
|
|
34
|
-
`@codefast/di-testing` requires Node.js 24 or later and a peer install of `@codefast/di
|
|
35
|
-
TypeScript setup: native Stage 3 decorators, `experimentalDecorators` off
|
|
36
|
-
|
|
36
|
+
`@codefast/di-testing` requires Node.js 24 or later, TypeScript 7 or later, and a peer install of `@codefast/di`, with
|
|
37
|
+
the same TypeScript setup: native Stage 3 decorators, `experimentalDecorators` off, and the explicit resource management
|
|
38
|
+
types, from `@types/node` 24 or later or from `ESNext.Disposable` in `lib`. The package is published on 0.x and
|
|
39
|
+
versioned on its own track: breaking changes ship as minor versions, so pin the minor version when you need stability.
|
|
37
40
|
|
|
38
41
|
## Quick start
|
|
39
42
|
|
|
40
43
|
```ts
|
|
41
44
|
import { injectable, token } from "@codefast/di";
|
|
42
|
-
import {
|
|
45
|
+
import { createTestBed } from "@codefast/di-testing";
|
|
43
46
|
import { expect, it, vi } from "vitest";
|
|
44
47
|
|
|
48
|
+
// The suite's entry point on Vitest spies, so matchers and mockReturnValue work on every mock.
|
|
49
|
+
const TestBed = createTestBed({ mockFactory: () => vi.fn() });
|
|
50
|
+
|
|
45
51
|
interface UserService {
|
|
46
52
|
findUser(id: string): { id: string; email: string };
|
|
47
53
|
}
|
|
@@ -73,7 +79,7 @@ class OrderProcessor {
|
|
|
73
79
|
}
|
|
74
80
|
|
|
75
81
|
it("charges then emails a confirmation", () => {
|
|
76
|
-
const { unit, mocks } = TestBed.solitary(OrderProcessor
|
|
82
|
+
const { unit, mocks } = TestBed.solitary(OrderProcessor)
|
|
77
83
|
.mock(UserServiceToken)
|
|
78
84
|
.stub((fn) => ({ findUser: fn().mockReturnValue({ id: "u1", email: "alice@example.com" }) }))
|
|
79
85
|
.compile();
|
|
@@ -85,8 +91,8 @@ it("charges then emails a confirmation", () => {
|
|
|
85
91
|
});
|
|
86
92
|
```
|
|
87
93
|
|
|
88
|
-
The zero-dependency default
|
|
89
|
-
|
|
94
|
+
The zero-dependency default is the `TestBed` export itself. Assert against the built-in spy's `.mock.calls` and stub
|
|
95
|
+
with `.mockReturnValue()`:
|
|
90
96
|
|
|
91
97
|
```ts
|
|
92
98
|
import { TestBed } from "@codefast/di-testing";
|
|
@@ -97,14 +103,29 @@ unit.placeOrder("u1", 42);
|
|
|
97
103
|
assert.deepEqual(mocks.get(PaymentGatewayToken).charge.mock.calls[0], ["u1", 42]);
|
|
98
104
|
```
|
|
99
105
|
|
|
100
|
-
##
|
|
106
|
+
## Entry points
|
|
107
|
+
|
|
108
|
+
A suite states its mock backend once. `createTestBed(options)` returns the entry point every bed in the suite begins
|
|
109
|
+
from — typically created in one shared test-support module:
|
|
101
110
|
|
|
102
|
-
|
|
103
|
-
|
|
111
|
+
```ts
|
|
112
|
+
import { createTestBed } from "@codefast/di-testing";
|
|
113
|
+
import { vi } from "vitest";
|
|
114
|
+
|
|
115
|
+
export const TestBed = createTestBed({ mockFactory: () => vi.fn() });
|
|
116
|
+
```
|
|
104
117
|
|
|
105
|
-
- `mockFactory
|
|
118
|
+
- `mockFactory: () => spy` — required: the spy backend each auto-mock is built from. Its return type is the entry
|
|
119
|
+
point's backend and types every bed it begins. Pass `defaultMockFactory` for the built-in spy.
|
|
106
120
|
- `metadataReader?: MetadataReader` — the reader dependencies are discovered through. Defaults to di's reader.
|
|
107
121
|
|
|
122
|
+
The `TestBed` export is `createTestBed({ mockFactory: defaultMockFactory })`: the built-in spy, no framework needed.
|
|
123
|
+
|
|
124
|
+
## Solitary beds
|
|
125
|
+
|
|
126
|
+
`TestBed.solitary(target)` begins a bed for `target` and auto-mocks every dependency it declares with the entry point's
|
|
127
|
+
backend. Nothing is instantiated until you call `compile()`.
|
|
128
|
+
|
|
108
129
|
The builder records overrides, then compiles:
|
|
109
130
|
|
|
110
131
|
- `.mock(token).stub((fn) => stub)` — bind a partial stub built from the active spy factory; unlisted members stay
|
|
@@ -127,14 +148,17 @@ The builder records overrides, then compiles:
|
|
|
127
148
|
## Sociable beds
|
|
128
149
|
|
|
129
150
|
A sociable bed keeps chosen collaborators real while everything else stays mocked — a unit test over a small real
|
|
130
|
-
subtree, not an integration test. `TestBed.sociable(target
|
|
131
|
-
`.expose()`, because a sociable bed with nothing exposed is a solitary bed.
|
|
151
|
+
subtree, not an integration test. `TestBed.sociable(target)` mocks with the entry point's backend like `solitary` and
|
|
152
|
+
returns only `.expose()`, because a sociable bed with nothing exposed is a solitary bed.
|
|
132
153
|
|
|
133
154
|
```ts
|
|
134
155
|
import { injectable, token } from "@codefast/di";
|
|
135
|
-
import {
|
|
156
|
+
import { createTestBed } from "@codefast/di-testing";
|
|
136
157
|
import { expect, it, vi } from "vitest";
|
|
137
158
|
|
|
159
|
+
// The suite's entry point on Vitest spies, so matchers and mockReturnValue work on every mock.
|
|
160
|
+
const TestBed = createTestBed({ mockFactory: () => vi.fn() });
|
|
161
|
+
|
|
138
162
|
interface TaxPolicy {
|
|
139
163
|
rateFor(currency: string): number;
|
|
140
164
|
}
|
|
@@ -160,7 +184,7 @@ class CheckoutService {
|
|
|
160
184
|
}
|
|
161
185
|
|
|
162
186
|
it("prices through the real PricingService over a mocked tax boundary", () => {
|
|
163
|
-
const bed = TestBed.sociable(CheckoutService
|
|
187
|
+
const bed = TestBed.sociable(CheckoutService)
|
|
164
188
|
.expose(PricingService)
|
|
165
189
|
.mock(TaxPolicyToken)
|
|
166
190
|
.stub((fn) => ({ rateFor: fn().mockReturnValue(0.1) }))
|
|
@@ -206,10 +230,10 @@ it("prices through the real PricingService over a mocked tax boundary", () => {
|
|
|
206
230
|
- `dispose()` — run the unit's `@preDestroy` hooks and dispose the container.
|
|
207
231
|
|
|
208
232
|
The bed implements `AsyncDisposable`, so `await using bed = TestBed.solitary(X).compile()` disposes it at the end of the
|
|
209
|
-
block
|
|
233
|
+
block.
|
|
210
234
|
|
|
211
235
|
The lower-level pieces are exported too: `createAutoMock`, `createSpy`, `defaultMockFactory`, and the `Mocked`,
|
|
212
|
-
`DeepPartial`, `MockFactory`, and `
|
|
236
|
+
`DeepPartial`, `MockFactory`, `Spy`, and `TestBedOptions` types.
|
|
213
237
|
|
|
214
238
|
## Errors
|
|
215
239
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Reads the dependencies a class declares, the input every auto-mock is built from. */
|
|
2
|
-
import { NotInjectableError } from "
|
|
2
|
+
import { NotInjectableError } from "#errors/errors";
|
|
3
3
|
/**
|
|
4
4
|
* Reads every dependency a class declares — constructor parameters first, then accessor injections.
|
|
5
5
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Binds a unit's discovered dependencies onto a container as mocks — the sole container coupling. */
|
|
2
2
|
import type { BindingTag, Constructor, Container, DependencyKey, DependencySlot, InjectOptions } from "@codefast/di";
|
|
3
|
-
import type { MockFactory } from "
|
|
3
|
+
import type { MockFactory } from "#mocking/mock-factory";
|
|
4
4
|
/**
|
|
5
5
|
* How one dependency is supplied instead of a plain auto-mock.
|
|
6
6
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Binds a unit's discovered dependencies onto a container as mocks — the sole container coupling. */
|
|
2
2
|
import { slotName, tokenName } from "@codefast/di";
|
|
3
|
-
import { OverrideMismatchError, UndeclaredDependencyError } from "
|
|
4
|
-
import { createAutoMock } from "
|
|
3
|
+
import { OverrideMismatchError, UndeclaredDependencyError } from "#errors/errors";
|
|
4
|
+
import { createAutoMock } from "#mocking/auto-mock";
|
|
5
5
|
/** The name prefix `usingAll` elements are bound under, so identical constants keep distinct slots. */
|
|
6
6
|
const ALL_ELEMENT_SLOT_PREFIX = "di-testing:all:";
|
|
7
7
|
/**
|
package/dist/errors/errors.d.ts
CHANGED
|
@@ -21,6 +21,18 @@ export declare class NotInjectableError extends TestingError {
|
|
|
21
21
|
readonly targetName: string;
|
|
22
22
|
constructor(targetName: string);
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* A `createTestBed(...)` call with no mock factory to build its beds' mocks from.
|
|
26
|
+
*
|
|
27
|
+
* @remarks The types require the factory; this reaches a caller the compiler cannot, and stops the
|
|
28
|
+
* built-in spy from standing in for a backend nobody chose.
|
|
29
|
+
*
|
|
30
|
+
* @since 0.2.0
|
|
31
|
+
*/
|
|
32
|
+
export declare class MissingMockFactoryError extends TestingError {
|
|
33
|
+
readonly code = "MISSING_MOCK_FACTORY";
|
|
34
|
+
constructor();
|
|
35
|
+
}
|
|
24
36
|
/**
|
|
25
37
|
* A `.mock(...)` override or a `mocks.get(...)` lookup named a token or slot the unit does not use.
|
|
26
38
|
*
|
package/dist/errors/errors.js
CHANGED
|
@@ -26,6 +26,20 @@ export class NotInjectableError extends TestingError {
|
|
|
26
26
|
this.targetName = targetName;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* A `createTestBed(...)` call with no mock factory to build its beds' mocks from.
|
|
31
|
+
*
|
|
32
|
+
* @remarks The types require the factory; this reaches a caller the compiler cannot, and stops the
|
|
33
|
+
* built-in spy from standing in for a backend nobody chose.
|
|
34
|
+
*
|
|
35
|
+
* @since 0.2.0
|
|
36
|
+
*/
|
|
37
|
+
export class MissingMockFactoryError extends TestingError {
|
|
38
|
+
code = "MISSING_MOCK_FACTORY";
|
|
39
|
+
constructor() {
|
|
40
|
+
super("createTestBed() needs a mockFactory to build every bed's mocks from: pass defaultMockFactory for the built-in spy, or a factory such as () => vi.fn().");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
29
43
|
/**
|
|
30
44
|
* A `.mock(...)` override or a `mocks.get(...)` lookup named a token or slot the unit does not use.
|
|
31
45
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/** Solitary and sociable auto-mocking test beds for `@codefast/di`. */
|
|
2
|
-
export { TestBed } from "
|
|
3
|
-
export type { TestBedStatic } from "
|
|
4
|
-
export type { MockOverrideBuilder, PreparedBed, TestBedOptions } from "
|
|
5
|
-
export type { SolitaryTestBedBuilder } from "
|
|
6
|
-
export type { SociableTestBedBuilder } from "
|
|
7
|
-
export type { SociableUnitTestBed, UnitReference, UnitTestBed } from "
|
|
8
|
-
export { createAutoMock, MOCK_RESET } from "
|
|
9
|
-
export type { DeepPartial, Mocked } from "
|
|
10
|
-
export { defaultMockFactory } from "
|
|
11
|
-
export type { MockFactory, MockFunction } from "
|
|
12
|
-
export { createSpy } from "
|
|
13
|
-
export type { Spy, SpyResult, SpyState } from "
|
|
14
|
-
export type { InjectionIdentifier } from "
|
|
15
|
-
export { ExposureError, NotInjectableError, OverrideMismatchError, SealedDependencyError, TestingError, UndeclaredDependencyError, } from "
|
|
16
|
-
export type { SealedCause } from "
|
|
2
|
+
export { createTestBed, TestBed } from "#test-bed/test-bed";
|
|
3
|
+
export type { TestBedStatic } from "#test-bed/test-bed";
|
|
4
|
+
export type { MockOverrideBuilder, PreparedBed, TestBedOptions } from "#test-bed/bed-builder";
|
|
5
|
+
export type { SolitaryTestBedBuilder } from "#test-bed/solitary-builder";
|
|
6
|
+
export type { SociableTestBedBuilder } from "#test-bed/sociable-builder";
|
|
7
|
+
export type { SociableUnitTestBed, UnitReference, UnitTestBed } from "#test-bed/unit-test-bed";
|
|
8
|
+
export { createAutoMock, MOCK_RESET } from "#mocking/auto-mock";
|
|
9
|
+
export type { DeepPartial, Mocked } from "#mocking/auto-mock";
|
|
10
|
+
export { defaultMockFactory } from "#mocking/mock-factory";
|
|
11
|
+
export type { MockFactory, MockFunction } from "#mocking/mock-factory";
|
|
12
|
+
export { createSpy } from "#mocking/spy";
|
|
13
|
+
export type { Spy, SpyResult, SpyState } from "#mocking/spy";
|
|
14
|
+
export type { InjectionIdentifier } from "#types";
|
|
15
|
+
export { MissingMockFactoryError, ExposureError, NotInjectableError, OverrideMismatchError, SealedDependencyError, TestingError, UndeclaredDependencyError, } from "#errors/errors";
|
|
16
|
+
export type { SealedCause } from "#errors/errors";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Solitary and sociable auto-mocking test beds for `@codefast/di`. */
|
|
2
|
-
export { TestBed } from "
|
|
3
|
-
export { createAutoMock, MOCK_RESET } from "
|
|
4
|
-
export { defaultMockFactory } from "
|
|
5
|
-
export { createSpy } from "
|
|
6
|
-
export { ExposureError, NotInjectableError, OverrideMismatchError, SealedDependencyError, TestingError, UndeclaredDependencyError, } from "
|
|
2
|
+
export { createTestBed, TestBed } from "#test-bed/test-bed";
|
|
3
|
+
export { createAutoMock, MOCK_RESET } from "#mocking/auto-mock";
|
|
4
|
+
export { defaultMockFactory } from "#mocking/mock-factory";
|
|
5
|
+
export { createSpy } from "#mocking/spy";
|
|
6
|
+
export { MissingMockFactoryError, ExposureError, NotInjectableError, OverrideMismatchError, SealedDependencyError, TestingError, UndeclaredDependencyError, } from "#errors/errors";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** The lazy `Proxy` that mocks an erased interface one accessed property at a time. */
|
|
2
|
-
import type { MockFactory, MockFunction } from "
|
|
3
|
-
import type { Spy } from "
|
|
2
|
+
import type { MockFactory, MockFunction } from "#mocking/mock-factory";
|
|
3
|
+
import type { Spy } from "#mocking/spy";
|
|
4
4
|
/**
|
|
5
5
|
* A mocked view of `Dependency`: every member becomes a spy, nested objects are mocked in turn.
|
|
6
6
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The pluggable seam that decides which spy backend the auto-mocks are built from. */
|
|
2
|
-
import { createSpy } from "
|
|
2
|
+
import { createSpy } from "#mocking/spy";
|
|
3
3
|
/**
|
|
4
4
|
* The default `MockFactory` — one built-in {@link Spy} per call, with no test-framework dependency.
|
|
5
5
|
*
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
/** The override-recording core both test-bed builders extend. */
|
|
2
2
|
import type { Constructor, Container, DependencyKey, InjectOptions, MetadataReader } from "@codefast/di";
|
|
3
|
-
import type { BoundMock, SlottedOverride } from "
|
|
4
|
-
import type { DeepPartial } from "
|
|
5
|
-
import type { MockFactory, MockFunction } from "
|
|
6
|
-
import type { Spy } from "
|
|
7
|
-
import type { InjectionIdentifier } from "
|
|
3
|
+
import type { BoundMock, SlottedOverride } from "#discovery/mock-binder";
|
|
4
|
+
import type { DeepPartial } from "#mocking/auto-mock";
|
|
5
|
+
import type { MockFactory, MockFunction } from "#mocking/mock-factory";
|
|
6
|
+
import type { Spy } from "#mocking/spy";
|
|
7
|
+
import type { InjectionIdentifier } from "#types";
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* What `createTestBed` is configured with: the mock backend and the reader every bed it begins shares.
|
|
10
10
|
*
|
|
11
11
|
* @typeParam Backend - The spy type the mock factory produces; it flows into every `Mocked` member,
|
|
12
12
|
* `mocks.get`, and the `.stub` callback, so `() => vi.fn()` yields Vitest's own mock typing.
|
|
13
13
|
*
|
|
14
14
|
* @since 0.1.0
|
|
15
15
|
*/
|
|
16
|
-
export interface TestBedOptions<Backend extends MockFunction
|
|
17
|
-
/** Spy factory each auto-mock property is materialized with;
|
|
18
|
-
readonly mockFactory
|
|
16
|
+
export interface TestBedOptions<Backend extends MockFunction> {
|
|
17
|
+
/** Spy factory each auto-mock property is materialized with; its return type is the beds' backend. */
|
|
18
|
+
readonly mockFactory: MockFactory<Backend>;
|
|
19
19
|
/** Reader the dependency scan and the compile container both consult; defaults to di's reader. */
|
|
20
20
|
readonly metadataReader?: MetadataReader | undefined;
|
|
21
21
|
}
|
|
@@ -74,7 +74,7 @@ export declare abstract class BedBuilder<Class, Backend extends MockFunction = S
|
|
|
74
74
|
protected readonly reader: MetadataReader;
|
|
75
75
|
protected readonly mockFactory: MockFactory<Backend>;
|
|
76
76
|
protected readonly overrides: ReadonlyMap<DependencyKey, ReadonlyArray<SlottedOverride>>;
|
|
77
|
-
constructor(target: Constructor<Class>, options
|
|
77
|
+
constructor(target: Constructor<Class>, options: TestBedOptions<Backend>);
|
|
78
78
|
/**
|
|
79
79
|
* Replaces the auto-mock for one dependency with a hand-written stub or a concrete value.
|
|
80
80
|
*
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/** The override-recording core both test-bed builders extend. */
|
|
2
2
|
import { defaultMetadataReader } from "@codefast/di";
|
|
3
3
|
import { verifyingMetadataReader } from "@codefast/di/metadata/verifying-metadata-reader";
|
|
4
|
-
import { criteriaEquals, normalizeCriteria } from "
|
|
5
|
-
import { defaultMockFactory } from "#/mocking/mock-factory";
|
|
4
|
+
import { criteriaEquals, normalizeCriteria } from "#discovery/mock-binder";
|
|
6
5
|
/**
|
|
7
6
|
* The shared builder core: records overrides, resolves the reader and mock factory, and owns the
|
|
8
7
|
* compile template that disposes the container when a build fails.
|
|
@@ -23,9 +22,8 @@ export class BedBuilder {
|
|
|
23
22
|
constructor(target, options) {
|
|
24
23
|
this.target = target;
|
|
25
24
|
// A supplied reader is a claim — verify it the way the container itself does.
|
|
26
|
-
this.reader = verifyingMetadataReader(options
|
|
27
|
-
|
|
28
|
-
this.mockFactory = options?.mockFactory ?? defaultMockFactory;
|
|
25
|
+
this.reader = verifyingMetadataReader(options.metadataReader ?? defaultMetadataReader);
|
|
26
|
+
this.mockFactory = options.mockFactory;
|
|
29
27
|
}
|
|
30
28
|
/**
|
|
31
29
|
* Replaces the auto-mock for one dependency with a hand-written stub or a concrete value.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** The fluent builder that compiles a unit together with chosen real collaborators. */
|
|
2
2
|
import type { Constructor, InjectOptions } from "@codefast/di";
|
|
3
|
-
import type { MockFunction } from "
|
|
4
|
-
import type { Spy } from "
|
|
5
|
-
import type { MockOverrideBuilder } from "
|
|
6
|
-
import { BedBuilder } from "
|
|
7
|
-
import type { SociableUnitTestBed } from "
|
|
8
|
-
import type { InjectionIdentifier } from "
|
|
3
|
+
import type { MockFunction } from "#mocking/mock-factory";
|
|
4
|
+
import type { Spy } from "#mocking/spy";
|
|
5
|
+
import type { MockOverrideBuilder } from "#test-bed/bed-builder";
|
|
6
|
+
import { BedBuilder } from "#test-bed/bed-builder";
|
|
7
|
+
import type { SociableUnitTestBed } from "#test-bed/unit-test-bed";
|
|
8
|
+
import type { InjectionIdentifier } from "#types";
|
|
9
9
|
/**
|
|
10
10
|
* A sociable build in progress: expose real collaborators, override the rest, then compile.
|
|
11
11
|
*
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/** The fluent builder that compiles a unit together with chosen real collaborators. */
|
|
2
2
|
import { Container, token } from "@codefast/di";
|
|
3
|
-
import { scanSociableDependencies } from "
|
|
4
|
-
import { bindMocks } from "
|
|
5
|
-
import { ExposureError } from "
|
|
6
|
-
import { BedBuilder } from "
|
|
7
|
-
import { createSociableUnitTestBed, createUnitTestBed } from "
|
|
3
|
+
import { scanSociableDependencies } from "#discovery/dependency-scanner";
|
|
4
|
+
import { bindMocks } from "#discovery/mock-binder";
|
|
5
|
+
import { ExposureError } from "#errors/errors";
|
|
6
|
+
import { BedBuilder } from "#test-bed/bed-builder";
|
|
7
|
+
import { createSociableUnitTestBed, createUnitTestBed } from "#test-bed/unit-test-bed";
|
|
8
8
|
/**
|
|
9
9
|
* The default {@link SociableTestBedBuilder}, backed by a fresh container per compile.
|
|
10
10
|
*
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** The fluent builder that compiles a solitary unit under test. */
|
|
2
2
|
import type { InjectOptions } from "@codefast/di";
|
|
3
|
-
import type { MockFunction } from "
|
|
4
|
-
import type { Spy } from "
|
|
5
|
-
import type { MockOverrideBuilder } from "
|
|
6
|
-
import { BedBuilder } from "
|
|
7
|
-
import type { UnitTestBed } from "
|
|
8
|
-
import type { InjectionIdentifier } from "
|
|
3
|
+
import type { MockFunction } from "#mocking/mock-factory";
|
|
4
|
+
import type { Spy } from "#mocking/spy";
|
|
5
|
+
import type { MockOverrideBuilder } from "#test-bed/bed-builder";
|
|
6
|
+
import { BedBuilder } from "#test-bed/bed-builder";
|
|
7
|
+
import type { UnitTestBed } from "#test-bed/unit-test-bed";
|
|
8
|
+
import type { InjectionIdentifier } from "#types";
|
|
9
9
|
/**
|
|
10
10
|
* A solitary build in progress: register overrides, then compile.
|
|
11
11
|
*
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/** The fluent builder that compiles a solitary unit under test. */
|
|
2
2
|
import { Container } from "@codefast/di";
|
|
3
|
-
import { scanDependencies } from "
|
|
4
|
-
import { bindMocks } from "
|
|
5
|
-
import { BedBuilder } from "
|
|
6
|
-
import { createUnitTestBed } from "
|
|
3
|
+
import { scanDependencies } from "#discovery/dependency-scanner";
|
|
4
|
+
import { bindMocks } from "#discovery/mock-binder";
|
|
5
|
+
import { BedBuilder } from "#test-bed/bed-builder";
|
|
6
|
+
import { createUnitTestBed } from "#test-bed/unit-test-bed";
|
|
7
7
|
/**
|
|
8
8
|
* The default {@link SolitaryTestBedBuilder}, backed by a fresh container per compile.
|
|
9
9
|
*
|
|
@@ -1,38 +1,48 @@
|
|
|
1
|
-
/** The entry
|
|
1
|
+
/** The entry points for building isolated units under test. */
|
|
2
2
|
import type { Constructor } from "@codefast/di";
|
|
3
|
-
import type { MockFunction } from "
|
|
4
|
-
import type { Spy } from "
|
|
5
|
-
import type { TestBedOptions } from "
|
|
6
|
-
import type { SociableTestBedBuilder } from "
|
|
7
|
-
import type { SolitaryTestBedBuilder } from "
|
|
3
|
+
import type { MockFunction } from "#mocking/mock-factory";
|
|
4
|
+
import type { Spy } from "#mocking/spy";
|
|
5
|
+
import type { TestBedOptions } from "#test-bed/bed-builder";
|
|
6
|
+
import type { SociableTestBedBuilder } from "#test-bed/sociable-builder";
|
|
7
|
+
import type { SolitaryTestBedBuilder } from "#test-bed/solitary-builder";
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* Begins test beds for classes under test, every one of them mocked with the same backend.
|
|
10
|
+
*
|
|
11
|
+
* @typeParam Backend - The spy type every bed's mocks are built with, fixed when the entry point is created.
|
|
10
12
|
*
|
|
11
13
|
* @since 0.1.0
|
|
12
14
|
*/
|
|
13
|
-
export interface TestBedStatic {
|
|
15
|
+
export interface TestBedStatic<Backend extends MockFunction> {
|
|
14
16
|
/**
|
|
15
17
|
* Begins a solitary test bed for `target`, auto-mocking every dependency it declares.
|
|
16
|
-
*
|
|
17
|
-
* @remarks The mock factory's return type becomes `Backend` and types every mock the bed hands
|
|
18
|
-
* out — pass `{ mockFactory: () => vi.fn() }` and `mocks.get(X).method` carries Vitest's own
|
|
19
|
-
* mock surface.
|
|
20
18
|
*/
|
|
21
|
-
solitary<Class
|
|
19
|
+
solitary<Class>(target: Constructor<Class>): SolitaryTestBedBuilder<Class, Backend>;
|
|
22
20
|
/**
|
|
23
21
|
* Begins a sociable test bed for `target`: chosen class collaborators stay real, tokens stay mocked.
|
|
24
22
|
*
|
|
25
23
|
* @remarks Returns only `expose` — a sociable bed without at least one exposed collaborator is a
|
|
26
24
|
* solitary bed, so the type steers the first call.
|
|
27
25
|
*/
|
|
28
|
-
sociable<Class
|
|
26
|
+
sociable<Class>(target: Constructor<Class>): Pick<SociableTestBedBuilder<Class, Backend>, "expose">;
|
|
29
27
|
}
|
|
30
28
|
/**
|
|
31
|
-
*
|
|
29
|
+
* Creates the test-bed entry point for one mock backend, so a suite states its backend once.
|
|
30
|
+
*
|
|
31
|
+
* @remarks The factory's return type becomes `Backend` and types every mock the beds hand out —
|
|
32
|
+
* `createTestBed({ mockFactory: () => vi.fn() })` gives `mocks.get(X).method` Vitest's own mock
|
|
33
|
+
* surface. A bed can have no other backend than the entry point it was begun from.
|
|
34
|
+
*
|
|
35
|
+
* @throws MissingMockFactoryError When no factory is passed, which only a caller past the types can do.
|
|
36
|
+
*
|
|
37
|
+
* @since 0.2.0
|
|
38
|
+
*/
|
|
39
|
+
export declare function createTestBed<Backend extends MockFunction>(options: TestBedOptions<Backend>): TestBedStatic<Backend>;
|
|
40
|
+
/**
|
|
41
|
+
* The test-bed entry point on the built-in spy, the backend with no test-framework dependency.
|
|
32
42
|
*
|
|
33
|
-
* @remarks `solitary` and `sociable` record the target
|
|
34
|
-
*
|
|
43
|
+
* @remarks `solitary` and `sociable` record the target only; nothing is instantiated until
|
|
44
|
+
* `compile()`. A suite on another backend creates its own with `createTestBed`.
|
|
35
45
|
*
|
|
36
46
|
* @since 0.1.0
|
|
37
47
|
*/
|
|
38
|
-
export declare const TestBed: TestBedStatic
|
|
48
|
+
export declare const TestBed: TestBedStatic<Spy>;
|
|
@@ -1,19 +1,37 @@
|
|
|
1
|
-
/** The entry
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
/** The entry points for building isolated units under test. */
|
|
2
|
+
import { MissingMockFactoryError } from "#errors/errors";
|
|
3
|
+
import { defaultMockFactory } from "#mocking/mock-factory";
|
|
4
|
+
import { SociableBuilder } from "#test-bed/sociable-builder";
|
|
5
|
+
import { SolitaryBuilder } from "#test-bed/solitary-builder";
|
|
4
6
|
/**
|
|
5
|
-
*
|
|
7
|
+
* Creates the test-bed entry point for one mock backend, so a suite states its backend once.
|
|
6
8
|
*
|
|
7
|
-
* @remarks
|
|
8
|
-
*
|
|
9
|
+
* @remarks The factory's return type becomes `Backend` and types every mock the beds hand out —
|
|
10
|
+
* `createTestBed({ mockFactory: () => vi.fn() })` gives `mocks.get(X).method` Vitest's own mock
|
|
11
|
+
* surface. A bed can have no other backend than the entry point it was begun from.
|
|
12
|
+
*
|
|
13
|
+
* @throws MissingMockFactoryError When no factory is passed, which only a caller past the types can do.
|
|
14
|
+
*
|
|
15
|
+
* @since 0.2.0
|
|
16
|
+
*/
|
|
17
|
+
export function createTestBed(options) {
|
|
18
|
+
// Read once, so a later write to the caller's object cannot change the beds already begun.
|
|
19
|
+
const { mockFactory, metadataReader } = options;
|
|
20
|
+
if (typeof mockFactory !== "function") {
|
|
21
|
+
throw new MissingMockFactoryError();
|
|
22
|
+
}
|
|
23
|
+
const resolved = { mockFactory, metadataReader };
|
|
24
|
+
return {
|
|
25
|
+
solitary: (target) => new SolitaryBuilder(target, resolved),
|
|
26
|
+
sociable: (target) => new SociableBuilder(target, resolved),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The test-bed entry point on the built-in spy, the backend with no test-framework dependency.
|
|
31
|
+
*
|
|
32
|
+
* @remarks `solitary` and `sociable` record the target only; nothing is instantiated until
|
|
33
|
+
* `compile()`. A suite on another backend creates its own with `createTestBed`.
|
|
9
34
|
*
|
|
10
35
|
* @since 0.1.0
|
|
11
36
|
*/
|
|
12
|
-
export const TestBed = {
|
|
13
|
-
solitary(target, options) {
|
|
14
|
-
return new SolitaryBuilder(target, options);
|
|
15
|
-
},
|
|
16
|
-
sociable(target, options) {
|
|
17
|
-
return new SociableBuilder(target, options);
|
|
18
|
-
},
|
|
19
|
-
};
|
|
37
|
+
export const TestBed = createTestBed({ mockFactory: defaultMockFactory });
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/** The compiled result of a test bed: the real unit plus handles to its mocks. */
|
|
2
2
|
import type { Constructor, Container, DependencyKey, InjectOptions, TokenValue } from "@codefast/di";
|
|
3
|
-
import type { BoundMock } from "
|
|
4
|
-
import type { Mocked } from "
|
|
5
|
-
import type { MockFunction } from "
|
|
6
|
-
import type { Spy } from "
|
|
3
|
+
import type { BoundMock } from "#discovery/mock-binder";
|
|
4
|
+
import type { Mocked } from "#mocking/auto-mock";
|
|
5
|
+
import type { MockFunction } from "#mocking/mock-factory";
|
|
6
|
+
import type { Spy } from "#mocking/spy";
|
|
7
7
|
/**
|
|
8
8
|
* A lookup from a dependency's token or class to the mock the unit was built with.
|
|
9
9
|
*
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/** The compiled result of a test bed: the real unit plus handles to its mocks. */
|
|
2
2
|
import { tokenName } from "@codefast/di";
|
|
3
|
-
import { criteriaEquals, normalizeCriteria } from "
|
|
4
|
-
import { ExposureError, SealedDependencyError, UndeclaredDependencyError } from "
|
|
5
|
-
import { MOCK_RESET } from "
|
|
3
|
+
import { criteriaEquals, normalizeCriteria } from "#discovery/mock-binder";
|
|
4
|
+
import { ExposureError, SealedDependencyError, UndeclaredDependencyError } from "#errors/errors";
|
|
5
|
+
import { MOCK_RESET } from "#mocking/auto-mock";
|
|
6
6
|
/**
|
|
7
7
|
* Wraps a compiled bed with access to its exposed real collaborators.
|
|
8
8
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codefast/di-testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Solitary and sociable auto-mocking test beds for @codefast/di",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"auto-mock",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"module": "./dist/index.js",
|
|
37
37
|
"types": "./dist/index.d.ts",
|
|
38
38
|
"imports": {
|
|
39
|
-
"
|
|
39
|
+
"#*": {
|
|
40
40
|
"types": "./dist/*.d.ts",
|
|
41
41
|
"default": "./dist/*.js"
|
|
42
42
|
}
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"access": "public"
|
|
101
101
|
},
|
|
102
102
|
"peerDependencies": {
|
|
103
|
-
"@codefast/di": ">=0.
|
|
103
|
+
"@codefast/di": ">=0.11.0"
|
|
104
104
|
},
|
|
105
105
|
"engines": {
|
|
106
106
|
"node": ">=24.0.0"
|