When mocking an Effect.Service or Context.Tag in tests,
you often want full type safety while providing only a subset of method overrides.
makeThrowingMock creates a fail‑fast default implementation where every method throws by default,
and you selectively override only what you need per test case.
import { Effect } from "effect";
/**
* Creates a fail-fast mock object for a service where every method throws
* an error if not explicitly overridden.
*
* @param serviceName - The name of the service (used in error messages).
* @returns An object with the same shape as the service, where every method
* is an Effect that dies with a "not implemented" message.
*/
export function makeThrowingMock<T extends object>(
serviceName: string
): { [K in keyof T]: T[K] } {
return new Proxy({} as T, {
get(_, prop) {
return () =>
Effect.dieMessage(
`${serviceName}.${String(prop)} not implemented in this mock`
);
},
});
}Here’s a complete example showing how to define an Effect.Service and create a mock with makeThrowingMock.
import { Layer, Effect } from "effect";
// 1. Define the service
class Logger extends Effect.Service<Logger>()("Logger", {
accessors: true,
effect: Effect.succeed({
info: (msg: string) => Effect.sync(() => console.log(msg)),
warn: (msg: string) => Effect.sync(() => console.warn(msg)),
error: (msg: string) => Effect.sync(() => console.error(msg)),
}),
}) {}
// 2. Create a fail-fast mock and override only what you need
const LoggerMock = Layer.mock(
Logger,
{
...makeThrowingMock<Logger>("Logger"),
info: (msg: string) => Effect.sync(() => console.log(`[MOCKED]: ${msg}`)),
}
);
// 3. Use in tests
const program = Logger.info("hello");
Effect.runSync(program);
// → [MOCKED]: hello
Effect.runSync(Logger.warn("oops"));
// → 💥 throws: Logger.warn not implemented in this mock- Type‑Safe — If the service signature changes, TypeScript will tell you.
- Fail‑Fast — Unimplemented methods throw immediately in tests.
- Minimal Boilerplate — Override only the methods you care about.
For extra convenience, wrap the throwing base with overrides in one call:
export function makeThrowingMockWithOverrides<T extends object>(
serviceName: string,
overrides: Partial<T>
): T {
return { ...makeThrowingMock<T>(serviceName), ...overrides } as T;
}
const LoggerMock2 = Layer.mock(
Logger,
makeThrowingMockWithOverrides<Logger>("Logger", {
info: (msg) => Effect.sync(() => console.log(`[MOCKED]: ${msg}`)),
})
);