ᯓ𝘁𝗿𝗮𝘃𝗲𝗿𝘀𝗮𝗯𝗹𝗲/𝘇𝗼𝗱-𝘁𝗲𝘀𝘁
Testing utility that generates arbitrary, pseudorandom zod schemas, powered by fast-check
Requirements
@traversable/zod-test has 2 peer dependencies:
zod(v4)fast-check
Usage
$ pnpm add -D @traversable/zod-test zod fast-check
Here's an example of importing the library:
import { z } from 'zod' import { zxTest } from '@traversable/zod-test' // see below for specifc examples
Track record
@traversabe/zod-test has found several upstream bugs in zod:
- Security exploit:
z.objectpollutes the globalObjectprototype
- Bug:
z.literalescaping bug
- Bug: "Diagonal" objects passed to
z.enumproduce false negatives
- Bug:
z.fileoutput type incompatible withglobalThis.File
Table of contents
zxTest.fuzzzxTest.seedToSchemazxTest.seedToValidDatazxTest.seedToInvalidDatazxTest.seedToValidDataGeneratorzxTest.seedToInvalidDataGeneratorzxTest.SeedGeneratorzxTest.SeedValidDataGeneratorzxTest.SeedInvalidDataGenerator
zxTest.fuzz
Convert a Zod schema into a fast-check arbitrary.
Configure how fuzzed values will be generated via the 2nd argument (options).
Override individual arbitraries via the 3rd argument (overrides).
Note
zxTest.fuzz is the only schema-to-generator function that has itself
been fuzz tested to ensure that no matter what schema you give it, the data-generator it
returns will always produce valid data.
This excludes schemas that make it impossible to generate valid data, for example:
z.neverz.nonoptional(z.undefined())z.enum([])z.union([])z.intersection(z.number(), z.string())
Example
import * as vi from 'vitest' import * as fc from 'fast-check' import { fuzz } from '@traversable/zod-test' const Schema = z.record( z.string(), z.union( z.number(), z.string(), ) ) const generator = fuzz( Schema, { record: { minKeys: 1 }, number: { noDefaultInfinity: true } }, { string: () => fc.stringMatching(/[\S\s]+[\S]+/) }, ) vi.test('fuzz test example', () => { fc.assert( fc.property(generator, (data) => { vi.assert.doesNotThrow(() => Schema.parse(data)) }), { numRuns: 1_000 } ) })
See also
- the fast-check docs
zxTest.seedToSchema
Use zxTest.seedToSchema to convert a seed generated by zxTest.SeedGenerator into a
zod schema that satisfies the configuration options you specified.
Example
import { zxTest } from '@traversable/zod-test' import * as fc from 'fast-check' const builder = zxTest.SeedGenerator()['*'] const [mySeed] = fc.sample(builder.object, 1) const mySchema = zxTest.seedToSchema(mySeed) // ^? const mySchema: z.ZodType
zxTest.seedToValidData
Use zxTest.seedToValidData to convert a seed generated by zxTest.SeedGenerator into
data that satisfies the schema that the seed represents.
Example
import { zxTest } from '@traversable/zod-test' import * as fc from 'fast-check' const builder = zxTest.SeedGenerator()['*'] const [mySeed] = fc.sample(builder.object, 1) const mySchema = zxTest.seedToSchema(mySeed) // ^? const mySchema: z.ZodType const validData = zxTest.seedToValidData(mySeed) mySchema.parse(validData) // will never throw
zxTest.seedToInvalidData
Use zxTest.seedToInvalidData to convert a seed generated by zxTest.SeedGenerator into
data that does not satisfy the schema that the seed represents.
Example
import { zxTest } from '@traversable/zod-test' import * as fc from 'fast-check' const builder = zxTest.SeedGenerator()['*'] const [mySeed] = fc.sample(builder.object, 1) const mySchema = zxTest.seedToSchema(mySeed) // ^? const mySchema: z.ZodType const invalidData = zxTest.seedToValidData(mySeed) mySchema.parse(invalidData) // should always throw
zxTest.seedToValidDataGenerator
Like zxTest.seedToValidData, except zxTest.seedToValidDataGenerator accepts a seed and returns a valid data arbitrary (which can then be used to produce valid data).
Example
import { zxTest } from '@traversable/zod-test' import * as fc from 'fast-check' const builder = zxTest.SeedGenerator()['*'] const [mySeed] = fc.sample(builder.object, 1) const mySchema = zxTest.seedToSchema(mySeed) // ^? const mySchema: z.ZodType const validDataGenerator = zxTest.seedToValidDataGenerator(mySeed) const [validData] = fc.sample(validDataGenerator, 1) mySchema.parse(validData) // will never throw
zxTest.seedToInvalidDataGenerator
Like zxTest.seedToInvalidData, except zxTest.seedToValidDataGenerator accepts a seed and returns an invalid data arbitrary (which can then be used to produce invalid data).
Example
import type * as z from 'zod' import * as fc from 'fast-check' import { zxTest } from '@traversable/zod-test' const builder = zxTest.SeedGenerator()['*'] const [mySeed] = fc.sample(builder.object, 1) const mySchema = zxTest.seedToSchema(mySeed) // ^? const mySchema: z.ZodType const invalidDataGenerator = zxTest.seedToInvalidDataGenerator(mySeed) const [invalidData] = fc.sample(invalidDataGenerator, 1) mySchema.parse(invalidData) // will always throw
zxTest.SeedGenerator
Note
zxTest.SeedGenerator is fairly low-level. All of the other exports of this library have been implemented in terms of zxTest.SeedGenerator.
Generates a configurable, pseudo-random "seed builder".
- Use
zxTest.seedToSchemato convert a seed into a zod schema - Use
zxTest.seedToValidDatato convert a seed into valid data - Use
zxTest.seedToInvalidDatato convert a seed into invalid data
Example
import { zxTest } from '@traversable/zod-test' import * as fc from 'fast-check' const builder = zxTest.SeedGenerator({ include: ["boolean", "string", "object"], // 𐙘 use `include` to only include certain schema types exclude: ["boolean", "any"], // 𐙘 use `exclude` to exclude certain schema types altogether (overrides `include`) object: { maxKeys: 5 }, // 𐙘 specific arbitraries are configurable by name }) // included schemas are present as properties on your generator... builder.string builder.object // ...excluded schemas are not present... builder.boolean // 🚫 TypeError // ...a special wildcard `"*"` property (pronounced "surprise me") is always present: builder["*"] /** * `fast-check` will generate a seed, which is a data structure containing * integers that represent a kind of AST. * * To use a seed, you need to pass it to an interpreter like `zxTest.seedToSchema`, * `zxTest.seedToValidData` or `zxTest.seedToInvalidData`: */ const [mySeed] = fc.sample(builder.object, 1) const mySchema = zxTest.seedToSchema(mySeed) // ^? const mySchema: z.ZodType const validData = zxTest.seedToValidData(mySeed) // ^? since the `mySeed` was also used to generate `mySchema`, // parsing `validData` should always succeed const invalidData = zxTest.seedToInvalidData(mySeed) // ^? since the `mySeed` was also used to generate `mySchema`, // parsing `invalidData` should always fail
zxTest.SeedValidDataGenerator
Like zxTest.SeedGenerator, except zxTest.SeedValidDataGenerator comes pre-configured to exclude schemas that make it impossible to reliably generate valid data.
Note
zxTest.SeedValidDataGenerator does not accept any options. If you need more fine-grained control of the schemas being generated, use zxTest.SeedGenerator.
zxTest.SeedInvalidDataGenerator
Like zxTest.SeedGenerator, except zxTest.SeedValidDataGenerator comes pre-configured to exclude schemas that make it impossible to reliably generate invalid data.
Note
zxTest.SeedInvalidDataGenerator does not accept any options. If you need more fine-grained control of the schemas being generated, use zxTest.SeedGenerator.