mockServerFn

Mocks a server function for testing client components in isolation. Creates a replacement function that uses the provided implementation and records all calls for assertions.

Import

import { mockServerFn } from 'catmint/testing'

Signature

function mockServerFn<TInput, TOutput>(
  original: (input: TInput) => Promise<TOutput>,
  implementation: (input: TInput) => Promise<TOutput>,
): MockedServerFn<TInput, TOutput>
interface MockedServerFn<TInput, TOutput> {
  (input: TInput): Promise<TOutput>
  calls: Array<{ input: TInput; result: TOutput }>
  reset: () => void
}

Parameters

ParameterTypeRequiredDescription
original(input: TInput) => Promise<TOutput>YesThe original server function to mock. Used for type inference; not called at runtime.
implementation(input: TInput) => Promise<TOutput>YesThe mock implementation that will be called instead of the original.

Return Value

Returns a MockedServerFn that is callable like the original function and has additional properties:

PropertyTypeDescription
callsArray<{ input: TInput; result: TOutput }>Array of all recorded calls, each containing the input that was passed and the result that was returned.
reset() => voidClears the call history.

Examples

Basic mock

import { mockServerFn } from 'catmint/testing'
import { getUser } from './queries'

const mockGetUser = mockServerFn(getUser, async (input) => {
  return { id: input.id, name: 'Test User', email: 'test@example.com' }
})

test('fetches a user', async () => {
  const user = await mockGetUser({ id: '123' })

  expect(user.name).toBe('Test User')
  expect(mockGetUser.calls).toHaveLength(1)
  expect(mockGetUser.calls[0].input).toEqual({ id: '123' })
})

Asserting multiple calls

const mockSave = mockServerFn(saveItem, async (input) => {
  return { ...input, savedAt: new Date().toISOString() }
})

await mockSave({ title: 'First' })
await mockSave({ title: 'Second' })

expect(mockSave.calls).toHaveLength(2)
expect(mockSave.calls[0].input.title).toBe('First')
expect(mockSave.calls[1].input.title).toBe('Second')

Resetting between tests

const mockDelete = mockServerFn(deleteItem, async (input) => {
  return { deleted: true }
})

afterEach(() => {
  mockDelete.reset()
})

test('first test', async () => {
  await mockDelete({ id: '1' })
  expect(mockDelete.calls).toHaveLength(1)
})

test('second test', async () => {
  // calls are reset — starts fresh
  expect(mockDelete.calls).toHaveLength(0)
})

See Also