Skip to content

Check shapes with schemas and matchers

Some values change on every run, like ids and dates. For those, check the shape instead of the exact value. In the expected body of assert, any value can be replaced with:

  • a schema from any Standard Schema library: Zod, Valibot, ArkType, Effect Schema. eberly does not depend on any of them;
  • a vitest or jest asymmetric matcher: expect.any(Number), expect.stringMatching(/@/).

You can mix them with exact values at any depth (posts.test.ts):

const created = await alice.posts.create({ body: { title: 'Hello', content: 'First post' } })
created.assert(201, {
title: 'Hello', // exact value
id: z.number().int().positive(), // any Standard Schema: Zod, Valibot, ArkType…
createdAt: z.iso.datetime(),
publishedAt: null,
authorId: expect.any(Number), // a vitest asymmetric matcher works too
})
// A schema can check the whole body; fields it does not list are ignored
created.assert(201, z.object({ id: z.number(), title: z.string().min(1) }))
  • Schemas are typed. The output type of a schema must fit the field: id: z.email() on a numeric id is a type error. Matchers are typed as any, so TypeScript does not check them.
  • The check stays partial. A schema for the whole body passes when the body has fields the schema does not list. To reject extra fields, use a strict schema, such as z.strictObject.
  • Errors point at the field. A failed schema reports the path, the message from the schema and the actual value: mismatch at "title": expected a value matching the zod schema (Invalid email address), got "Hello".
  • Schemas must be sync. An async schema, such as Zod with an async refine, throws an error.

Privacy-friendly analytics, no cookies.