Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    djankies

    testing-zod-schemas

    djankies/testing-zod-schemas
    Coding

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

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

    SKILL.md

    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
    Repository
    djankies/claude-configs
    Files