Claude Code Plugins

Community-maintained marketplace

Feedback

testing-zod-schemas

@djankies/claude-configs
0
0

Test Zod schemas comprehensively with unit tests, integration tests, and type tests for validation logic

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name testing-zod-schemas
description Test Zod schemas comprehensively with unit tests, integration tests, and type tests for validation logic

Testing Zod Schemas

Purpose

Comprehensive guide to testing Zod v4 schemas, including validation logic, error messages, transformations, and type inference.

For Vitest test structure, mocking, and async patterns, use vitest-4/skills/writing-vitest-tests

Unit Testing Schemas

Basic Validation Tests

import { z } from 'zod';

const userSchema = z.object({
  email: z.email().trim().toLowerCase(),
  age: z.number().min(18),
  username: z.string().trim().min(3)
});

const result = userSchema.safeParse({
  email: 'user@example.com',
  age: 25,
  username: 'john'
});

expect(result.success).toBe(true);
if (result.success) {
  expect(result.data.email).toBe('user@example.com');
}

const invalidResult = userSchema.safeParse({
  email: 'not-an-email',
  age: 25,
  username: 'john'
});

expect(invalidResult.success).toBe(false);
if (!invalidResult.success) {
  expect(invalidResult.error.issues[0].path).toEqual(['email']);
}

Testing Transformations

const emailSchema = z.email().trim().toLowerCase();

const result = emailSchema.safeParse('  USER@EXAMPLE.COM  ');

expect(result.success).toBe(true);
if (result.success) {
  expect(result.data).toBe('user@example.com');
}

Testing Error Messages

const schema = z.object({
  email: z.email({ error: "Please enter a valid email address" }),
  password: z.string().min(8, {
    error: "Password must be at least 8 characters"
  })
});

const result = schema.safeParse({
  email: 'invalid',
  password: 'password123'
});

expect(result.success).toBe(false);
if (!result.success) {
  expect(result.error.issues[0].message).toBe(
    "Please enter a valid email address"
  );
}

Testing Refinements

const passwordSchema = z.string()
  .min(8)
  .refine(
    (password) => /[A-Z]/.test(password),
    { error: "Must contain uppercase letter" }
  )
  .refine(
    (password) => /[0-9]/.test(password),
    { error: "Must contain number" }
  );

const validResult = passwordSchema.safeParse('Password123');
expect(validResult.success).toBe(true);

const invalidResult = passwordSchema.safeParse('password123');
expect(invalidResult.success).toBe(false);
if (!invalidResult.success) {
  expect(invalidResult.error.issues[0].message).toBe(
    "Must contain uppercase letter"
  );
}

Testing Async Refinements

const emailSchema = z.email().refine(
  async (email) => {
    const exists = await checkEmailExists(email);
    return !exists;
  },
  { error: "Email already exists" }
);

const validResult = await emailSchema.safeParseAsync('new@example.com');
expect(validResult.success).toBe(true);

const invalidResult = await emailSchema.safeParseAsync('existing@example.com');
expect(invalidResult.success).toBe(false);
if (!invalidResult.success) {
  expect(invalidResult.error.issues[0].message).toBe("Email already exists");
}

Testing Complex Schemas

Nested Objects

const addressSchema = z.object({
  street: z.string().trim().min(1),
  city: z.string().trim().min(1),
  zip: z.string().trim().regex(/^\d{5}$/)
});

const userSchema = z.object({
  name: z.string().trim().min(1),
  address: addressSchema
});

const result = userSchema.safeParse({
  name: 'John',
  address: { street: '123 Main St', city: 'Boston', zip: 'invalid' }
});

expect(result.success).toBe(false);
if (!result.success) {
  expect(result.error.issues[0].path).toEqual(['address', 'zip']);
}

Arrays

const tagsSchema = z.array(
  z.string().trim().min(1)
).min(1, { error: "At least one tag required" });

const result = tagsSchema.safeParse(['valid', '']);

expect(result.success).toBe(false);
if (!result.success) {
  expect(result.error.issues[0].path).toEqual([1]);
}

Discriminated Unions

const eventSchema = z.discriminatedUnion('type', [
  z.object({
    type: z.literal('click'),
    x: z.number(),
    y: z.number()
  }),
  z.object({
    type: z.literal('keypress'),
    key: z.string()
  })
]);

const result = eventSchema.safeParse({
  type: 'click',
  x: 100,
  y: 200
});

expect(result.success).toBe(true);

Type Testing

Type Inference

const userSchema = z.object({
  email: z.email(),
  age: z.number(),
  name: z.string()
});

type User = z.infer<typeof userSchema>;

expectTypeOf<User>().toEqualTypeOf<{
  email: string;
  age: number;
  name: string;
}>();

Transform Types

const schema = z.string().transform(s => parseInt(s));

type Input = z.input<typeof schema>;
type Output = z.output<typeof schema>;

expectTypeOf<Input>().toEqualTypeOf<string>();
expectTypeOf<Output>().toEqualTypeOf<number>();

Best Practices

1. Test Both Success and Failure

Always test valid data passes and invalid data fails

2. Test Transformations

Verify trim, lowercase, and other transforms produce expected output

3. Verify Error Messages

Check custom error messages appear correctly

4. Test Edge Cases

Handle empty strings, very long strings, special characters

5. Use SafeParse in Tests

const result = schema.safeParse(data);  // ✅
try { schema.parse(data) }              // ❌

6. Test Type Inference

Verify z.infer, z.input, and z.output produce correct types

Test Coverage

Aim for:

  • 100% branch coverage for validation logic
  • 100% path coverage for refinements
  • Edge cases tested thoroughly
  • Error messages verified
  • Transformations validated

For coverage configuration in Vitest 4.x when testing schemas, use vitest-4/skills/configuring-vitest-4 for coverage include patterns and thresholds setup.

References

  • v4 Features: Use the validating-string-formats skill from the zod-4 plugin
  • Error handling: Use the customizing-errors skill from the zod-4 plugin
  • Transformations: Use the transforming-string-methods skill from the zod-4 plugin
  • Performance: Use the optimizing-performance skill from the zod-4 plugin

Cross-Plugin References:

  • If testing Zod validation with React components, use the testing-components skill for component integration testing patterns
  • @vitest-4/skills/configuring-vitest-4 - Coverage configuration for schema testing

Success Criteria

  • ✅ 100% branch coverage for validation logic
  • ✅ Success and failure paths tested
  • ✅ Transformations verified
  • ✅ Error messages validated
  • ✅ Edge cases covered
  • ✅ Type inference tested
  • ✅ Integration tests pass
  • ✅ Performance benchmarks meet targets