setupCraftServiceTestingByRegister
Sets up a craftService or toCraftService from an explicit flat register derived from the full dependency graph.
Import
import { setupCraftServiceTestingByRegister } from '@craft-ng/core';Introduction
setupCraftServiceTestingByRegister is the exhaustive testing utility for the craftService graph.
Instead of providing only the overrides you care about, you provide a full typed register where each service is marked as:
- real
- provided by its raw
provideX(...) - mocked with a raw object
- or pruned with
'notReached'
This is useful when you want explicit control over every node in the service graph.
Register Workflow
The intended workflow is:
- start from the full dependency graph of the SUT
- fill each key with a real provider,
'real', a mock object, or'notReached' - pass the register to
setupCraftServiceTestingByRegister(...)
Basic Example
import {
craftService,
setupCraftServiceTestingByRegister,
state,
} from '@craft-ng/core';
import { vi } from 'vitest';
const { CounterToYield } = craftService(
{ name: 'Counter', scope: 'global' },
() =>
state(10, ({ update }) => ({
increment: () => update((value) => value + 1),
})),
);
const { injectCounterConsumer, provideCounterConsumer } = craftService(
{ name: 'CounterConsumer', scope: 'toProvide' },
function* () {
const counter = yield* CounterToYield();
return {
read: () => counter(),
increment: () => counter.increment(),
};
},
);
const { sut, mocks } = setupCraftServiceTestingByRegister(
injectCounterConsumer,
{
CounterConsumer: provideCounterConsumer(),
Counter: {
$self: vi.fn(() => 41),
increment: vi.fn(),
},
},
);
expect(sut.read()).toBe(41);
sut.increment();
expect(mocks.Counter.increment).toHaveBeenCalledTimes(1);Register Semantics
'real'
Use 'real' for reachable non-provider scopes such as global or function.
setupCraftServiceTestingByRegister(injectCounterConsumer, {
CounterConsumer: provideCounterConsumer(),
Counter: 'real',
});Raw provider
Use the provider returned by provideX(...) for toProvide or manuallyProvidedAtRoot services.
setupCraftServiceTestingByRegister(injectRootCounter, {
RootCounter: provideRootCounter(),
ParentCounter: provideParentCounter(),
ChildCounter: provideChildCounter(),
});Raw mock object
Use a plain object when you want to override the public service shape.
setupCraftServiceTestingByRegister(injectCounterConsumer, {
CounterConsumer: provideCounterConsumer(),
Counter: {
$self: vi.fn(() => 12),
increment: vi.fn(),
},
});'notReached'
Use 'notReached' only when the service is on a branch fully pruned by an ancestor mock.
setupCraftServiceTestingByRegister(injectRootCounter, {
RootCounter: provideRootCounter(),
ParentCounter: {
incrementParent: vi.fn(),
},
ChildCounter: 'notReached',
});Return Value
The function returns:
sut: the resolved service under testmocks: only the services that were actually mocked in the register
Entries marked as 'real', 'notReached', or provided through raw providers are not exposed in mocks.
Extra Angular Providers
When the graph depends on real Angular infrastructure, append providers through the third argument.
const { sut } = setupCraftServiceTestingByRegister(injectNavigation, register, {
providers: [provideRouter([])],
});Alias
setupTestingService is a backward-compatible alias of setupCraftServiceTestingByRegister.