Skip to main content

Overview

Express Zod API provides utilities for testing endpoints and middleware without starting a server.
import { testEndpoint, testMiddleware } from "express-zod-api";

testEndpoint()

Tests an endpoint by mocking request/response objects.
import { testEndpoint } from "express-zod-api";

const { responseMock, loggerMock } = await testEndpoint({
  endpoint: getUserEndpoint,
  requestProps: {
    method: "GET",
    query: { id: "123" },
  },
});

expect(responseMock._getStatusCode()).toBe(200);
expect(responseMock._getJSONData()).toEqual({
  status: "success",
  data: { id: "123", name: "John" },
});

Parameters

endpoint
Endpoint
required
The endpoint to test
requestProps
RequestProps
Mock request properties
{
  method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
  query?: Record<string, any>;
  body?: any;
  params?: Record<string, string>;
  headers?: Record<string, string>;
  files?: Record<string, any>;
  // ... any Express Request property
}
responseOptions
ResponseOptions
Mock response options
{
  locals?: Record<string, any>;
}
configProps
Partial<CommonConfig>
Override config for this test
loggerProps
LoggerOverrides
Custom logger for testing

Returns

responseMock
MockResponse
Mocked response object with assertion methods:
  • _getStatusCode(): Get HTTP status code
  • _getJSONData(): Get parsed JSON response
  • _getHeaders(): Get response headers (lowercase)
  • _isJSON(): Check if response is JSON
  • _isEndCalled(): Check if response ended
  • _getRedirectUrl(): Get redirect location
loggerMock
MockLogger
Mocked logger with logs:
  • _getLogs(): Returns { debug: [], info: [], warn: [], error: [] }

Examples

Testing GET Endpoint

import { testEndpoint } from "express-zod-api";
import { describe, expect, test } from "vitest";

describe("GET /users/:id", () => {
  test("should return user", async () => {
    const { responseMock, loggerMock } = await testEndpoint({
      endpoint: getUserEndpoint,
      requestProps: {
        method: "GET",
        params: { id: "123" },
      },
    });

    expect(loggerMock._getLogs().error).toHaveLength(0);
    expect(responseMock._getStatusCode()).toBe(200);
    expect(responseMock._getJSONData()).toEqual({
      status: "success",
      data: {
        id: "123",
        name: "John Doe",
        email: "john@example.com",
      },
    });
  });

  test("should return 404 for missing user", async () => {
    const { responseMock } = await testEndpoint({
      endpoint: getUserEndpoint,
      requestProps: {
        params: { id: "999" },
      },
    });

    expect(responseMock._getStatusCode()).toBe(404);
  });
});

Testing POST Endpoint

test("should create user", async () => {
  const { responseMock } = await testEndpoint({
    endpoint: createUserEndpoint,
    requestProps: {
      method: "POST",
      body: {
        name: "Jane Doe",
        email: "jane@example.com",
      },
    },
  });

  expect(responseMock._getStatusCode()).toBe(201);
  expect(responseMock._getJSONData().data).toHaveProperty("id");
});

Testing File Upload

test("should upload file", async () => {
  const mockFile = {
    name: "test.pdf",
    data: Buffer.from("test content"),
    size: 12,
    mimetype: "application/pdf",
    mv: vi.fn().mockResolvedValue(undefined),
  };

  const { responseMock } = await testEndpoint({
    endpoint: uploadEndpoint,
    requestProps: {
      method: "POST",
      files: { document: mockFile },
    },
  });

  expect(responseMock._getStatusCode()).toBe(200);
  expect(mockFile.mv).toHaveBeenCalled();
});

testMiddleware()

Tests middleware in isolation.
import { testMiddleware } from "express-zod-api";

const { output, responseMock, loggerMock } = await testMiddleware({
  middleware: authMiddleware,
  requestProps: {
    headers: { authorization: "Bearer token123" },
  },
});

expect(output).toHaveProperty("user");
expect(output.user.id).toBe("123");

Parameters

middleware
Middleware
required
The middleware to test
requestProps
RequestProps
Mock request properties
responseOptions
ResponseOptions
Mock response options
ctx
Context
Context from previous middleware
configProps
Partial<CommonConfig>
Override config for this test
loggerProps
LoggerOverrides
Custom logger for testing

Returns

output
Context
Context object returned by middleware
responseMock
MockResponse
Mocked response object
loggerMock
MockLogger
Mocked logger

Examples

Testing Auth Middleware

import { testMiddleware } from "express-zod-api";
import createHttpError from "http-errors";

test("should authenticate valid token", async () => {
  const { output, responseMock } = await testMiddleware({
    middleware: authMiddleware,
    requestProps: {
      headers: { authorization: "Bearer valid-token" },
    },
  });

  expect(output.user).toBeDefined();
  expect(output.user.id).toBe("123");
});

test("should reject invalid token", async () => {
  const { responseMock } = await testMiddleware({
    middleware: authMiddleware,
    requestProps: {
      headers: { authorization: "Bearer invalid" },
    },
  });

  expect(responseMock._getStatusCode()).toBe(401);
});

Testing Middleware Chain

test("should pass context through chain", async () => {
  // Test first middleware
  const { output: firstOutput } = await testMiddleware({
    middleware: authMiddleware,
    requestProps: {
      headers: { authorization: "Bearer token" },
    },
  });

  // Test second middleware with previous context
  const { output: secondOutput } = await testMiddleware({
    middleware: permissionsMiddleware,
    ctx: firstOutput,
  });

  expect(secondOutput).toHaveProperty("user");
  expect(secondOutput).toHaveProperty("permissions");
});

Integration with Test Frameworks

Vitest

import { describe, test, expect } from "vitest";
import { testEndpoint } from "express-zod-api";

describe("Users API", () => {
  test("GET /users", async () => {
    const { responseMock } = await testEndpoint({
      endpoint: listUsersEndpoint,
    });
    expect(responseMock._getStatusCode()).toBe(200);
  });
});

Jest

import { testEndpoint } from "express-zod-api";

describe("Users API", () => {
  it("should list users", async () => {
    const { responseMock } = await testEndpoint({
      endpoint: listUsersEndpoint,
    });
    expect(responseMock._getStatusCode()).toBe(200);
  });
});

See Also