Testing and Mocking
Learn how to test oRPC procedures directly with server-side clients, create mock implementations with the implementer, and mock network requests with MSW.
Testing
For fast, focused tests, use Server-Side Clients or call your procedures directly with call. This lets you verify validation, middleware, and handler logic without going through HTTP.
import { call } from '@orpc/server'
it('lists planets', async () => {
await expect(
call(router.planet.list, { page: 1, size: 10 })
).resolves.toEqual([
{ id: '1', name: 'Earth' },
{ id: '2', name: 'Mars' },
])
})
Mocking
Use the Implementer to create test-specific versions of a procedure or router. This is useful when one part of your system depends on another procedure, but your test should not execute the real implementation.
import { function implement<TContract extends RouterContract, TInitialContext extends Context = DefaultInitialContext>(contract: TContract, config?: ProcedureConfig): Implementer<TContract, TInitialContext & object>Turns a contract into an implementer, used to implement the contract's
procedures, routers, and middleware with full type safety.implement } from '@orpc/server'
const const fakeListPlanet: ImplementedProcedure<DefaultInitialContext & object, object, ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>
fakeListPlanet = implement<ImplementedProcedure<{
headers?: IncomingHttpHeaders;
} & object, object, ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>, DefaultInitialContext>(contract: ImplementedProcedure<{
headers?: IncomingHttpHeaders;
} & object, object, ZodObject<...>, ZodArray<...>, object>, config?: ProcedureConfig): Implementer<...>
Turns a contract into an implementer, used to implement the contract's
procedures, routers, and middleware with full type safety.implement(const router: {
planet: {
list: ImplementedProcedure<{
headers?: IncomingHttpHeaders;
} & object, object, ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>;
find: ImplementedProcedure<{
headers?: IncomingHttpHeaders;
} & object, object, ZodObject<{
id: ZodNumber;
}, $strip>, ZodObject<...>, object>;
create: ImplementedProcedure<...>;
};
}
router.planet: {
list: ImplementedProcedure<{
headers?: IncomingHttpHeaders;
} & object, object, ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>;
find: ImplementedProcedure<{
headers?: IncomingHttpHeaders;
} & object, object, ZodObject<{
id: ZodNumber;
}, $strip>, ZodObject<...>, object>;
create: ImplementedProcedure<...>;
}
planet.list: ImplementedProcedure<{
headers?: IncomingHttpHeaders;
} & object, object, ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<ZodString>;
}, $strip>>, object>
list).ProcedureImplementer<DefaultInitialContext & object, object, ZodObject<{ limit: ZodOptional<ZodNumber>; cursor: ZodDefault<ZodNumber>; }, $strip>, ZodArray<...>, object>['handler'](handler: ProcedureHandler<DefaultInitialContext & object, {
cursor: number;
limit?: number | undefined;
}, {
id: number;
name: string;
description?: string | undefined;
}[] | AnyORPCError, object>): ImplementedProcedure<DefaultInitialContext & object, object, ZodObject<{
limit: ZodOptional<ZodNumber>;
cursor: ZodDefault<ZodNumber>;
}, $strip>, ZodArray<ZodObject<{
id: ZodNumber;
name: ZodString;
description: ZodOptional<...>;
}, $strip>>, object>
handler(() => [])
Use fakeListPlanet anywhere your test would normally use the real listPlanet procedure.
Mocking Network Requests
To mock at the network level instead, for example in Storybook stories or browser tests, use the MSW Integration. It turns a contract or router into typed MSW request handlers served through a real fetch handler:
import { createHTTPUtils } from '@orpc/experimental-msw'
import { RPCHandler } from '@orpc/server/fetch'
const mock = createHTTPUtils(router, {
prefix: '/rpc',
handler: router => new RPCHandler(router),
})
const handlers = [
mock.planet.list.handler(() => []),
]