Core Building Blocks / Command

Testing

Unit test handlers in isolation or validate the full runtime path with typed mocks.

PURISTA provides a dedicated test runner for commands. Import it from @purista/core and use it to test your command function in isolation with mocked resources.

Handler test with getCommandFunctionTestRunner

Use getCommandFunctionTestRunner to run your command function with injected mock resources:

import { describe, test, expect, vi } from 'vitest'
import { getCommandFunctionTestRunner } from '@purista/core'
import { signUpCommandBuilder } from './signUpCommand.js'

describe('signUp command', () => {
  test('creates a user and returns the userId', async () => {
    const mockDb = { insert: vi.fn().mockResolvedValue({ id: 'user-123' }) }

    const runner = await getCommandFunctionTestRunner(signUpCommandBuilder, {
      resources: { db: mockDb },
    })

    const result = await runner({ email: 'alice@example.com', password: 'secure1234' })

    expect(result.userId).toBeDefined()
    expect(mockDb.insert).toHaveBeenCalledOnce()
  })

  test('rejects disposable email addresses', async () => {
    const mockDb = { insert: vi.fn() }

    const runner = await getCommandFunctionTestRunner(signUpCommandBuilder, {
      resources: { db: mockDb },
    })

    await expect(
      runner({ email: 'bob@tempmail.com', password: 'secure1234' })
    ).rejects.toThrow()
  })
})

The runner:

  • Calls your command function directly with the provided payload
  • Injects the resources, secrets, configs, and states you pass in the options
  • Bypasses the full event bridge pipeline (no validation guards, no transforms)
  • Is fast and deterministic — ideal for unit testing business logic

Which level should you use?

Scenario Recommended approach
Business logic branching getCommandFunctionTestRunner
Resource interaction patterns getCommandFunctionTestRunner
Schema validation behavior Integration/runtime test
Guard hook integration Integration/runtime test
Transform hook integration Integration/runtime test
Full end-to-end flow Integration/runtime test

As a rule of thumb: write handler tests first using getCommandFunctionTestRunner. They are fast, deterministic, and cover the majority of your logic. Add integration tests for the pipeline stages (validation, guards, transforms) where multiple components interact.