Skip to main content

Steve Kinney

Testing Express APIs with Zod Fixtures and Superagent

Testing your Express API is crucial for ensuring its reliability and correctness. This guide demonstrates how to use zod-fixture to generate realistic test data based on your Zod schemas, and superagent to make HTTP requests to your API.

Install Dependencies

Install the necessary dependencies:

npm install superagent zod zod-fixture --save-dev
npm install @types/superagent --save-dev # if using TypeScript

This installs:

  • superagent: HTTP client for testing your API
  • zod: Schema validation library
  • zod-fixture: Generates test data from Zod schemas

Define Your Zod Schemas

Ensure that your Express API uses Zod schemas for request and response validation.

Example:

// schemas.ts
import { z } from 'zod';

export const createUserSchema = z.object({
  username: z.string().min(3),
  email: z.string().email(),
  age: z.number().int().positive().optional(),
});

export const userResponseSchema = z.object({
  id: z.string().uuid(),
  username: z.string(),
  email: z.string(),
  age: z.number().int().positive().optional(),
});

Create Zod Fixtures

Use zod-fixture to generate test data based on your Zod schemas.

Example:

// fixtures.ts
import { generateMock } from 'zod-fixture';
import { createUserSchema, userResponseSchema } from './schemas';

export const createUserFixture = () => generateMock(createUserSchema);
export const userResponseFixture = () => generateMock(userResponseSchema);

Write Test Cases

Use superagent to make HTTP requests to your Express API and zod-fixture to generate test data.

Example (using Jest):

// app.test.ts
import request from 'superagent';
import { createUserFixture, userResponseFixture } from './fixtures';
import { createUserSchema, userResponseSchema } from './schemas';
import express, { Request, Response } from 'express';
import { z } from 'zod';

const app = express();
app.use(express.json());

app.post(
  '/users',
  (
    req: Request<{}, {}, z.infer<typeof createUserSchema>>,
    res: Response<z.infer<typeof userResponseSchema>>,
  ) => {
    const newUser: z.infer<typeof userResponseSchema> = {
      id: '123e4567-e89b-12d3-a456-426614174000',
      username: req.body.username,
      email: req.body.email,
      age: req.body.age,
    };
    res.status(201).json(newUser);
  },
);

const server = app.listen(3001); // Use a different port for testing

describe('User API', () => {
  afterAll(() => {
    server.close();
  });

  it('should create a user', async () => {
    const userData = createUserFixture();
    const response = await request.post('http://localhost:3001/users').send(userData);

    expect(response.status).toBe(201);
    expect(response.body).toEqual(expect.objectContaining(userData));
    expect(userResponseSchema.safeParse(response.body).success).toBe(true);
  });

  it('should handle invalid input', async () => {
    const invalidUserData = { username: 'a', email: 'invalid-email' }; // Invalid username and email
    try {
      await request.post('http://localhost:3001/users').send(invalidUserData);
    } catch (error: any) {
      expect(error.response.status).toBe(400); // Assuming your API returns 400 for invalid input
    }
  });

  it('should generate a valid user response fixture', () => {
    const responseData = userResponseFixture();
    expect(userResponseSchema.safeParse(responseData).success).toBe(true);
  });
});

Run Tests

Run your tests using your chosen testing framework (e.g., Jest, Mocha).

npx jest app.test.ts

Benefits

  • Realistic Test Data: zod-fixture generates realistic test data based on your Zod schemas, ensuring that your tests closely resemble real-world scenarios.
  • Type Safety: Zod schemas and fixtures provide type safety, reducing the risk of errors.
  • Simplified Testing: superagent simplifies the process of making HTTP requests, making your tests more readable and maintainable.
  • Validation: You can easily validate the response body against your zod schemas.
  • Improved Reliability: Comprehensive testing improves the reliability of your Express API.

Last modified on .