Skip to content

How to Write Unit Tests for Valibot


Created: – Last Updated:

Valibot (opens in a new tab) is an open source schema library for TypeScript, similar to Zod. In this article, you’ll learn how to unit test your defined schemas using Valibot. You’ll use Vitest (opens in a new tab) for testing, but the approach should work with other test runners as well.

Let’s start with a sample Valibot schema:

schema.ts
import * as v from 'valibot';
const EmptyObjectSchema = v.strictObject({})
export function isEmpty(input: unknown) {
const result = v.safeParse(EmptyObjectSchema, input)
return result.success
}
export const EmailSchema = v.pipe(
v.string('Your email must be a string.'),
v.nonEmpty('Please enter your email.'),
v.email('The email address is badly formatted.')
);
export type EmailSchemaType = v.InferOutput<typeof EmailSchema>;

The isEmpty() function checks whether the input is an empty object. Meanwhile, EmailSchema validates that a valid email address is provided.

Testing isEmpty()

Testing isEmpty() is straightforward, as you’re not directly testing the Valibot schema itself. Since it uses safeParse(), no error is thrown, and you can simply assert the return value.

schema.test.ts
import { describe, it, expect } from "vitest"
import { isEmpty } from "./schema"
describe('isEmpty', () => {
it('should return true for an empty object', () => {
const input = {}
const result = isEmpty(input)
expect(result).toBe(true)
})
})

Testing EmailSchema with parse()

Testing the schema directly is more interesting. To do this, import parse() from Valibot. When you use parse(), your test will fail if the schema changes in a way that introduces a regression.

schema.test.ts
import { describe, it, expect } from "vitest"
import { parse } from 'valibot'
import { isEmpty, EmailSchema } from "./schema"
7 collapsed lines
describe('isEmpty', () => {
it('should return true for an empty object', () => {
const input = {}
const result = isEmpty(input)
expect(result).toBe(true)
})
})
describe('EmailSchema', () => {
it('should validate a valid email', () => {
const input = 'hello+world@gmail.com'
const result = parse(EmailSchema, input)
expect(result).toBe(input)
})
it('should return an error for non-string input', () => {
const input = 123
const error = 'Your email must be a string.'
expect(() => parse(EmailSchema, input)).toThrowError(error)
})
})

When the input is valid, Valibot returns it unchanged — this is verified in the first test case. If you’ve defined custom error messages, ensure you test them by asserting that parse() throws the expected error. Use toThrowError() from Vitest for that.

Testing Valibot TypeScript types

At the time of writing, testing types (opens in a new tab) with Vitest is still experimental. Here’s an explanation on how to typecheck Valibot’s inferred types:

schema.test.ts
import { describe, it, expect, expectTypeOf } from "vitest"
import { parse } from 'valibot'
import { isEmpty, EmailSchema, type EmailSchemaType } from "./schema"
22 collapsed lines
describe('isEmpty', () => {
it('should return true for an empty object', () => {
const input = {}
const result = isEmpty(input)
expect(result).toBe(true)
})
})
describe('EmailSchema', () => {
it('should validate a valid email', () => {
const input = 'hello+world@gmail.com'
const result = parse(EmailSchema, input)
expect(result).toBe(input)
})
it('should return an error for non-string input', () => {
const input = 123
const error = 'Your email must be a string.'
expect(() => parse(EmailSchema, input)).toThrowError(error)
})
})
describe('EmailSchemaType', () => {
it('should be a string', () => {
expectTypeOf<EmailSchemaType>().toEqualTypeOf<string>()
})
})