8. Test the graph
Goal: test Effect programs, their Layers and the Craft boundary without mocking the whole application.
Test an Effect service with a partial mock
mockEffectService provides a Layer and makes every unstubbed member fail loudly if the test accidentally uses it:
import { Effect } from 'effect';
import { mockEffectService } from '@craft-ts/effect';
const register = {
AccessPolicyService: mockEffectService(AccessPolicyService, {
decide: () => Effect.succeed(expectedDecision),
}),
};For production-like tests, provide the real Layer. For focused tests, stub only the selected members and let UnstubbedEffectMember expose an unexpected read.
Test the Craft service or component by register
Craft tests still use a register derived from the Craft dependency graph:
const { sut } = await setupCraftServiceTestingByRegister(
AccessDecisionService,
{
AccessPolicyService: mockEffectService(AccessPolicyService, {
decide: () => Effect.succeed(expectedDecision),
}),
},
);The exact register also includes regular Craft services, 'real', 'notReached' or provideX() entries when those nodes are reachable. Effect service mocks cover the Effect side; the Craft register proves the full graph is accounted for.
Test the bridge and adapters
Install and dispose the bridge per test suite:
let dispose: () => void;
beforeEach(() => {
dispose = installCraftEffectBridge();
});
afterEach(() => {
dispose();
});Cover at least one example of each channel: a typed failure becomes a Craft exception, Effect.die rejects as a technical error, and aborting the owning resource interrupts the Effect.
Architecture checks
The static graph includes the Effect backend automatically when analyzeDependencyGraph runs. It exposes typed nodes for effect-service, effect-operation and effect-layer, together with requires-service, provided-by-layer and composes-layer relations. You can therefore write rules against Effect concepts just as you do against Craft nodes:
const effectServices = graph.nodes('effect-service');
const effectOperations = graph.nodes('effect-operation');
const effectLayers = graph.nodes('effect-layer');
const serviceRequirements = graph.edges('requires-service');The built-in checks can then be kept beside the app's architecture tests:
assertCraftEffectNoNetwork(graph.graph);
assertCraftEffectNoImperativeSync(graph.graph);
assertDeclarativeArchitecture(graph.graph);For a project-specific invariant, inspect these typed nodes and relations in a custom assertion and fail the architecture test when the rule is violated. If you need to add concepts that the built-in Effect backend does not model, use the DependencyGraphNodeRegistry and DependencyGraphCollector. The Effect graph is already collected; no extra collector is needed just to apply rules to its services, operations or Layers.
What you gained
Tests that mirror the real Craft and Effect graphs: real composition by default, narrow mocks at the boundary, and architecture rules for the invariants that types alone cannot keep armed.