Testing a smithy-hono service
smithy-hono gives you two things that make a generated service testable with almost no boilerplate:
- A generated typed client (
<stem>.client.gen.ts) emitted alongside the router by the same codegen — so client and server agree on the wire contract by construction. @smithy-hono/test-kit— harnesses that drive that client against your router/pipeline in-process (no network), plus auth helpers, builders, and assertions.
The generated client
Every build emits a create<Service>Client(opts?) factory (disable with
"emitClient": false in smithy-build.json). It uses only Web-standard fetch:
import { createTodoClient } from './generated'
// Production: real fetch + base URL.
const client = createTodoClient({ baseUrl: 'https://api.example.com' })
const { item } = await client.CreateTodo({ body: { title: 'x' } }) // typed in/out
// Modeled errors are thrown as the generated error classes:
// try { await client.GetTodo({ id }) } catch (e) { if (e instanceof TodoNotFound) … }
Options: fetch (default globalThis.fetch), baseUrl (default ''), and headers
(a hook returning headers merged into every request — auth, tracing, etc.).
Testing with @smithy-hono/test-kit
npm i -D @smithy-hono/test-kit
Unit (router only, stand-in principal):
import { mountRouter, expectError } from '@smithy-hono/test-kit'
import { createTodoRouter, createTodoClient, TodoNotFound } from '../generated'
import { OPERATIONS } from '../generated/registry.gen'
const { client } = mountRouter({ router: createTodoRouter(ops), createClient: createTodoClient, operations: OPERATIONS })
await client.CreateTodo({ body: { title: 'x' } })
await expectError(() => client.GetTodo({ id: 'nope' }), TodoNotFound)
Integration (full security pipeline + in-memory stores, with auth helpers):
import { createTestHarness } from '@smithy-hono/test-kit'
const h = createTestHarness({ operations: OPERATIONS, router: createTodoRouter(ops), createClient: createTodoClient })
const { client } = await h.loginAs() // seeds a session, attaches cookie + CSRF
await client.CreateTodo({ body: { title: 'x' } })
const svc = await h.asService({ keyId, secret }) // HMAC-signs each request (@sigv4Hmac ops)
See packages/test-kit/README.md for the full surface (builders, assertions, MCP client,
asService, raw-request escape hatches). Worked examples live in
examples/todo-api/test/ — behavior.test.ts / auth.test.ts (unit),
harness-e2e.test.ts (integration), client-roundtrip.test.ts (client↔router),
mcp-auth-e2e.test.ts (MCP).