@codefast/di-testing 0.1.1 → 0.1.3

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.
Files changed (69) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +1 -1
  3. package/README.md +143 -66
  4. package/dist/discovery/dependency-scanner.d.ts +1 -2
  5. package/dist/discovery/dependency-scanner.js +1 -2
  6. package/dist/discovery/mock-binder.d.ts +1 -2
  7. package/dist/discovery/mock-binder.js +1 -2
  8. package/dist/errors/errors.d.ts +1 -2
  9. package/dist/errors/errors.js +1 -2
  10. package/dist/index.d.ts +1 -2
  11. package/dist/index.js +1 -2
  12. package/dist/mocking/auto-mock.d.ts +1 -2
  13. package/dist/mocking/auto-mock.js +1 -2
  14. package/dist/mocking/mock-factory.d.ts +1 -2
  15. package/dist/mocking/mock-factory.js +1 -2
  16. package/dist/mocking/spy.d.ts +1 -2
  17. package/dist/mocking/spy.js +1 -2
  18. package/dist/test-bed/bed-builder.d.ts +1 -2
  19. package/dist/test-bed/bed-builder.js +1 -2
  20. package/dist/test-bed/sociable-builder.d.ts +1 -2
  21. package/dist/test-bed/sociable-builder.js +1 -2
  22. package/dist/test-bed/solitary-builder.d.ts +1 -2
  23. package/dist/test-bed/solitary-builder.js +1 -2
  24. package/dist/test-bed/test-bed.d.ts +1 -2
  25. package/dist/test-bed/test-bed.js +1 -2
  26. package/dist/test-bed/unit-test-bed.d.ts +1 -2
  27. package/dist/test-bed/unit-test-bed.js +1 -2
  28. package/dist/types.d.ts +1 -2
  29. package/dist/types.js +1 -2
  30. package/package.json +7 -55
  31. package/dist/discovery/dependency-scanner.d.ts.map +0 -1
  32. package/dist/discovery/dependency-scanner.js.map +0 -1
  33. package/dist/discovery/mock-binder.d.ts.map +0 -1
  34. package/dist/discovery/mock-binder.js.map +0 -1
  35. package/dist/errors/errors.d.ts.map +0 -1
  36. package/dist/errors/errors.js.map +0 -1
  37. package/dist/index.d.ts.map +0 -1
  38. package/dist/index.js.map +0 -1
  39. package/dist/mocking/auto-mock.d.ts.map +0 -1
  40. package/dist/mocking/auto-mock.js.map +0 -1
  41. package/dist/mocking/mock-factory.d.ts.map +0 -1
  42. package/dist/mocking/mock-factory.js.map +0 -1
  43. package/dist/mocking/spy.d.ts.map +0 -1
  44. package/dist/mocking/spy.js.map +0 -1
  45. package/dist/test-bed/bed-builder.d.ts.map +0 -1
  46. package/dist/test-bed/bed-builder.js.map +0 -1
  47. package/dist/test-bed/sociable-builder.d.ts.map +0 -1
  48. package/dist/test-bed/sociable-builder.js.map +0 -1
  49. package/dist/test-bed/solitary-builder.d.ts.map +0 -1
  50. package/dist/test-bed/solitary-builder.js.map +0 -1
  51. package/dist/test-bed/test-bed.d.ts.map +0 -1
  52. package/dist/test-bed/test-bed.js.map +0 -1
  53. package/dist/test-bed/unit-test-bed.d.ts.map +0 -1
  54. package/dist/test-bed/unit-test-bed.js.map +0 -1
  55. package/dist/types.d.ts.map +0 -1
  56. package/dist/types.js.map +0 -1
  57. package/src/discovery/dependency-scanner.ts +0 -80
  58. package/src/discovery/mock-binder.ts +0 -334
  59. package/src/errors/errors.ts +0 -119
  60. package/src/index.ts +0 -26
  61. package/src/mocking/auto-mock.ts +0 -243
  62. package/src/mocking/mock-factory.ts +0 -35
  63. package/src/mocking/spy.ts +0 -92
  64. package/src/test-bed/bed-builder.ts +0 -172
  65. package/src/test-bed/sociable-builder.ts +0 -148
  66. package/src/test-bed/solitary-builder.ts +0 -70
  67. package/src/test-bed/test-bed.ts +0 -64
  68. package/src/test-bed/unit-test-bed.ts +0 -161
  69. package/src/types.ts +0 -15
@@ -1,70 +0,0 @@
1
- /** The fluent builder that compiles a solitary unit under test. */
2
-
3
- import type { InjectOptions } from "@codefast/di";
4
- import { Container } from "@codefast/di";
5
-
6
- import { scanDependencies } from "#/discovery/dependency-scanner";
7
- import { bindMocks } from "#/discovery/mock-binder";
8
- import type { MockFunction } from "#/mocking/mock-factory";
9
- import type { Spy } from "#/mocking/spy";
10
- import type { MockOverrideBuilder, PreparedBed } from "#/test-bed/bed-builder";
11
- import { BedBuilder } from "#/test-bed/bed-builder";
12
- import type { UnitTestBed } from "#/test-bed/unit-test-bed";
13
- import { createUnitTestBed } from "#/test-bed/unit-test-bed";
14
- import type { InjectionIdentifier } from "#/types";
15
-
16
- /**
17
- * A solitary build in progress: register overrides, then compile.
18
- *
19
- * @typeParam Class - The class under test.
20
- * @typeParam Backend - The spy type the bed's mock factory produces.
21
- *
22
- * @since 0.1.0
23
- */
24
- export interface SolitaryTestBedBuilder<Class, Backend extends MockFunction = Spy> {
25
- /** Replaces the auto-mock for one dependency with a hand-written stub or a concrete value. */
26
- mock<Dependency>(
27
- identifier: InjectionIdentifier<Dependency>,
28
- options?: InjectOptions,
29
- ): MockOverrideBuilder<Dependency, SolitaryTestBedBuilder<Class, Backend>, Backend>;
30
- /** Instantiates the unit with every dependency mocked, running accessor injection and `@postConstruct`. */
31
- compile(): UnitTestBed<Class, Backend>;
32
- /** Async variant for a unit whose `@postConstruct` is async; otherwise identical to {@link compile}. */
33
- compileAsync(): Promise<UnitTestBed<Class, Backend>>;
34
- }
35
-
36
- /**
37
- * The default {@link SolitaryTestBedBuilder}, backed by a fresh container per compile.
38
- *
39
- * @typeParam Class - The class under test.
40
- * @typeParam Backend - The spy type the bed's mock factory produces.
41
- *
42
- * @since 0.1.0
43
- */
44
- export class SolitaryBuilder<Class, Backend extends MockFunction = Spy>
45
- extends BedBuilder<Class, Backend>
46
- implements SolitaryTestBedBuilder<Class, Backend>
47
- {
48
- compile(): UnitTestBed<Class, Backend> {
49
- return this.compileWith(
50
- () => this.#prepare(),
51
- (unit, prepared) => createUnitTestBed(unit, prepared.mocks, prepared.container),
52
- );
53
- }
54
-
55
- async compileAsync(): Promise<UnitTestBed<Class, Backend>> {
56
- return this.compileWithAsync(
57
- () => this.#prepare(),
58
- (unit, prepared) => createUnitTestBed(unit, prepared.mocks, prepared.container),
59
- );
60
- }
61
-
62
- /** Builds the container, binds every dependency's mock, and registers the unit as a singleton. */
63
- #prepare(): PreparedBed {
64
- const container = Container.create({ metadataReader: this.reader });
65
- const dependencies = scanDependencies(this.target, this.reader);
66
- const mocks = bindMocks(container, dependencies, this.overrides, this.mockFactory);
67
- container.bind(this.target).toSelf().singleton();
68
- return { container, mocks };
69
- }
70
- }
@@ -1,64 +0,0 @@
1
- /** The entry point for building isolated units under test. */
2
-
3
- import type { Constructor } from "@codefast/di";
4
-
5
- import type { MockFunction } from "#/mocking/mock-factory";
6
- import type { Spy } from "#/mocking/spy";
7
- import type { TestBedOptions } from "#/test-bed/bed-builder";
8
- import { SociableBuilder } from "#/test-bed/sociable-builder";
9
- import type { SociableTestBedBuilder } from "#/test-bed/sociable-builder";
10
- import { SolitaryBuilder } from "#/test-bed/solitary-builder";
11
- import type { SolitaryTestBedBuilder } from "#/test-bed/solitary-builder";
12
-
13
- /**
14
- * The factory surface for building isolated units under test.
15
- *
16
- * @since 0.1.0
17
- */
18
- export interface TestBedStatic {
19
- /**
20
- * Begins a solitary test bed for `target`, auto-mocking every dependency it declares.
21
- *
22
- * @remarks The mock factory's return type becomes `Backend` and types every mock the bed hands
23
- * out — pass `{ mockFactory: () => vi.fn() }` and `mocks.get(X).method` carries Vitest's own
24
- * mock surface.
25
- */
26
- solitary<Class, Backend extends MockFunction = Spy>(
27
- target: Constructor<Class>,
28
- options?: TestBedOptions<Backend>,
29
- ): SolitaryTestBedBuilder<Class, Backend>;
30
-
31
- /**
32
- * Begins a sociable test bed for `target`: chosen class collaborators stay real, tokens stay mocked.
33
- *
34
- * @remarks Returns only `expose` — a sociable bed without at least one exposed collaborator is a
35
- * solitary bed, so the type steers the first call.
36
- */
37
- sociable<Class, Backend extends MockFunction = Spy>(
38
- target: Constructor<Class>,
39
- options?: TestBedOptions<Backend>,
40
- ): Pick<SociableTestBedBuilder<Class, Backend>, "expose">;
41
- }
42
-
43
- /**
44
- * Entry point for auto-mocking a class in isolation from its collaborators.
45
- *
46
- * @remarks `solitary` and `sociable` record the target and options only; nothing is instantiated
47
- * until `compile()`.
48
- *
49
- * @since 0.1.0
50
- */
51
- export const TestBed: TestBedStatic = {
52
- solitary<Class, Backend extends MockFunction = Spy>(
53
- target: Constructor<Class>,
54
- options?: TestBedOptions<Backend>,
55
- ): SolitaryTestBedBuilder<Class, Backend> {
56
- return new SolitaryBuilder<Class, Backend>(target, options);
57
- },
58
- sociable<Class, Backend extends MockFunction = Spy>(
59
- target: Constructor<Class>,
60
- options?: TestBedOptions<Backend>,
61
- ): Pick<SociableTestBedBuilder<Class, Backend>, "expose"> {
62
- return new SociableBuilder<Class, Backend>(target, options);
63
- },
64
- };
@@ -1,161 +0,0 @@
1
- /// <reference lib="esnext.disposable" />
2
-
3
- /** The compiled result of a test bed: the real unit plus handles to its mocks. */
4
-
5
- import type { Constructor, Container, DependencyKey, InjectOptions, TokenValue } from "@codefast/di";
6
- import { tokenName } from "@codefast/di";
7
-
8
- import type { BoundMock } from "#/discovery/mock-binder";
9
- import { criteriaEquals, normalizeCriteria } from "#/discovery/mock-binder";
10
- import { ExposureError, SealedDependencyError, UndeclaredDependencyError } from "#/errors/errors";
11
- import type { Mocked } from "#/mocking/auto-mock";
12
- import { MOCK_RESET } from "#/mocking/auto-mock";
13
- import type { MockFunction } from "#/mocking/mock-factory";
14
- import type { Spy } from "#/mocking/spy";
15
-
16
- /**
17
- * A lookup from a dependency's token or class to the mock the unit was built with.
18
- *
19
- * @typeParam Backend - The spy type the bed's mock factory produces.
20
- *
21
- * @since 0.1.0
22
- */
23
- export interface UnitReference<Backend extends MockFunction = Spy> {
24
- /**
25
- * Retrieves the mock bound for one of the unit's dependencies, typed to that dependency's value.
26
- *
27
- * @remarks Pass `options` (a name or tags) to address one slot of a token injected several ways.
28
- * Only auto-mocks and `.stub` seeds come back — a value supplied with `.using()`, `.absent()`, or
29
- * `.usingAll()` is sealed, and an exposed class is real, so neither has a mock surface for the
30
- * `Mocked` type to describe.
31
- *
32
- * @throws UndeclaredDependencyError When the identifier or slot is not one of the unit's dependencies.
33
- * @throws SealedDependencyError When the dependency was supplied as a sealed value or exposed.
34
- */
35
- get<Identifier extends DependencyKey>(
36
- identifier: Identifier,
37
- options?: InjectOptions,
38
- ): Mocked<TokenValue<Identifier>, Backend>;
39
- }
40
-
41
- /**
42
- * A compiled unit: the real class under test, plus a handle to every mock it received.
43
- *
44
- * @remarks Implements `AsyncDisposable`, so `await using bed = TestBed.solitary(X).compile()` runs
45
- * the unit's `@preDestroy` hooks and disposes the backing container at the end of the block.
46
- *
47
- * @typeParam Class - The class under test.
48
- * @typeParam Backend - The spy type the bed's mock factory produces.
49
- *
50
- * @since 0.1.0
51
- */
52
- export interface UnitTestBed<Class, Backend extends MockFunction = Spy> extends AsyncDisposable {
53
- readonly unit: Class;
54
- readonly mocks: UnitReference<Backend>;
55
- /** Clears the call history and configured behaviour of every auto-mock and stub the bed created. */
56
- resetMocks(this: void): void;
57
- /** Runs the unit's `@preDestroy` hooks and disposes the backing container. */
58
- dispose(this: void): Promise<void>;
59
- }
60
-
61
- /**
62
- * A sociable bed: the solitary surface plus access to the real collaborators it exposed.
63
- *
64
- * @typeParam Class - The class under test.
65
- * @typeParam Backend - The spy type the bed's mock factory produces.
66
- *
67
- * @since 0.1.0
68
- */
69
- export interface SociableUnitTestBed<Class, Backend extends MockFunction = Spy> extends UnitTestBed<Class, Backend> {
70
- /**
71
- * Returns the real instance of an exposed collaborator — the one the unit was actually built with.
72
- *
73
- * @throws ExposureError When the class was not exposed.
74
- */
75
- exposed<Real>(target: Constructor<Real>): Real;
76
- }
77
-
78
- /**
79
- * Wraps a compiled bed with access to its exposed real collaborators.
80
- *
81
- * @since 0.1.0
82
- */
83
- export function createSociableUnitTestBed<Class, Backend extends MockFunction = Spy>(
84
- bed: UnitTestBed<Class, Backend>,
85
- container: Container,
86
- exposedClasses: ReadonlySet<Constructor>,
87
- ): SociableUnitTestBed<Class, Backend> {
88
- // Copied member by member, so a future bed with accessor or prototype members cannot be dropped
89
- // silently by a spread.
90
- return {
91
- unit: bed.unit,
92
- mocks: bed.mocks,
93
- resetMocks: bed.resetMocks,
94
- dispose: bed.dispose,
95
- [Symbol.asyncDispose]: bed[Symbol.asyncDispose],
96
- exposed<Real>(target: Constructor<Real>): Real {
97
- if (!exposedClasses.has(target)) {
98
- throw new ExposureError(target.name, "the class was not exposed. Add .expose(Class) before compile().");
99
- }
100
- // Exposed classes are singletons the unit's construction already instantiated.
101
- return container.resolve(target);
102
- },
103
- };
104
- }
105
-
106
- /**
107
- * Assembles a {@link UnitTestBed} around an instantiated unit and its bound mock entries.
108
- *
109
- * @since 0.1.0
110
- */
111
- export function createUnitTestBed<Class, Backend extends MockFunction = Spy>(
112
- unit: Class,
113
- entries: ReadonlyMap<DependencyKey, ReadonlyArray<BoundMock>>,
114
- container: Container,
115
- ): UnitTestBed<Class, Backend> {
116
- const mocks: UnitReference<Backend> = {
117
- get<Identifier extends DependencyKey>(
118
- identifier: Identifier,
119
- options?: InjectOptions,
120
- ): Mocked<TokenValue<Identifier>, Backend> {
121
- const bound = entries.get(identifier);
122
- if (bound === undefined) {
123
- throw new UndeclaredDependencyError(tokenName(identifier));
124
- }
125
- const criteria = normalizeCriteria(options);
126
- const entry =
127
- criteria === undefined
128
- ? (bound.find((candidate) => candidate.criteria === undefined) ?? (bound.length === 1 ? bound[0] : undefined))
129
- : bound.find((candidate) => candidate.criteria !== undefined && criteriaEquals(candidate.criteria, criteria));
130
- if (entry === undefined) {
131
- throw new UndeclaredDependencyError(tokenName(identifier), "no such slot");
132
- }
133
- if (entry.kind !== "auto" && entry.kind !== "stub") {
134
- throw new SealedDependencyError(tokenName(identifier), entry.kind);
135
- }
136
- return entry.value as Mocked<TokenValue<Identifier>, Backend>;
137
- },
138
- };
139
-
140
- const resetMocks = (): void => {
141
- for (const bound of entries.values()) {
142
- for (const entry of bound) {
143
- if (entry.kind === "auto" || entry.kind === "stub") {
144
- (entry.value as { [MOCK_RESET]?: () => void })[MOCK_RESET]?.();
145
- }
146
- }
147
- }
148
- };
149
-
150
- const dispose = async (): Promise<void> => {
151
- await container.dispose();
152
- };
153
-
154
- return {
155
- unit,
156
- mocks,
157
- resetMocks,
158
- dispose,
159
- [Symbol.asyncDispose]: dispose,
160
- };
161
- }
package/src/types.ts DELETED
@@ -1,15 +0,0 @@
1
- /** Shared vocabulary the test bed keys its dependencies by. */
2
-
3
- import type { Constructor, Token } from "@codefast/di";
4
-
5
- /**
6
- * A token or class constructor used to identify one of the unit's dependencies.
7
- *
8
- * @remarks The value-typed counterpart of di's own `DependencyKey`: it carries `Value` so
9
- * `mocks.get()` can map an identifier back to the mock type it produced.
10
- *
11
- * @typeParam Value - The value type the identified dependency resolves to.
12
- *
13
- * @since 0.1.0
14
- */
15
- export type InjectionIdentifier<Value> = Token<Value> | Constructor<Value>;