Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cloudflare/workers-sdk/llms.txt

Use this file to discover all available pages before exploring further.

Cloudflare Workers SDK provides several testing utilities to help you write comprehensive tests for your Workers.

D1 Testing Utilities

applyD1Migrations()

Apply D1 migrations from a directory.
import { applyD1Migrations, env } from "cloudflare:test";
import { beforeAll, it, expect } from "vitest";

beforeAll(async () => {
  await applyD1Migrations(env.DB, "./migrations");
});

it("should have schema", async () => {
  const result = await env.DB.prepare(
    "SELECT name FROM sqlite_master WHERE type='table'"
  ).all();
  expect(result.results).toContainEqual({ name: "users" });
});
db
D1Database
required
D1 database binding
migrationsPath
string
required
Path to migrations directory containing SQL files
void
Promise<void>
Resolves when migrations are complete

listD1Databases()

List all D1 databases in local development.
import { listD1Databases } from "@cloudflare/vitest-pool-workers/config";

const databases = await listD1Databases(".wrangler/state/v3/d1");
console.log(databases); // ["my-db", "test-db"]
persistPath
string
required
Path to D1 persistence directory
databases
Promise<string[]>
Array of database names

getD1DatabasePath()

Get the SQLite file path for a D1 database.
import { getD1DatabasePath } from "@cloudflare/vitest-pool-workers/config";

const dbPath = getD1DatabasePath(".wrangler/state/v3/d1", "my-db");
console.log(dbPath); // ".wrangler/state/v3/d1/my-db.sqlite"
persistPath
string
required
Path to D1 persistence directory
databaseName
string
required
Database name
path
string
Full path to the SQLite database file

Pages Testing Utilities

getPagesAssetFetcher()

Get a fetcher for Pages assets.
import { getPagesAssetFetcher } from "@cloudflare/vitest-pool-workers/config";
import { expect, it } from "vitest";

it("should serve static files", async () => {
  const assetFetcher = getPagesAssetFetcher({
    directory: "./public",
  });

  const response = await assetFetcher.fetch(new Request("https://example.com/index.html"));
  expect(response.status).toBe(200);
  expect(response.headers.get("content-type")).toBe("text/html");
});
options
PagesAssetOptions
required
fetcher
Fetcher
Fetcher for serving static assets

readPagesConfig()

Read and parse a Pages configuration file.
import { readPagesConfig } from "@cloudflare/vitest-pool-workers/config";

const config = await readPagesConfig("./functions/_routes.json");
console.log(config.routes);
configPath
string
required
Path to Pages config file
config
Promise<PagesConfig>
Parsed Pages configuration

Durable Object Testing

createDurableObjectId()

Create a Durable Object ID for testing.
import { env, createDurableObjectId } from "cloudflare:test";
import { expect, it } from "vitest";

it("should create DO instances", async () => {
  const id1 = env.MY_DO.idFromName("test-1");
  const id2 = env.MY_DO.idFromName("test-2");
  
  const stub1 = env.MY_DO.get(id1);
  const stub2 = env.MY_DO.get(id2);
  
  // Each stub represents a different DO instance
  expect(stub1).not.toBe(stub2);
});

resetDurableObjectStorage()

Reset a Durable Object’s storage.
import { env, runInDurableObject } from "cloudflare:test";
import { it, expect, beforeEach } from "vitest";

let doId;

beforeEach(async () => {
  doId = env.MY_DO.idFromName("test");
  
  await runInDurableObject(env.MY_DO, doId, async (instance, state) => {
    await state.storage.deleteAll();
  });
});

it("should have empty storage", async () => {
  await runInDurableObject(env.MY_DO, doId, async (instance, state) => {
    const keys = await state.storage.list();
    expect(keys.size).toBe(0);
  });
});

Queue Testing

sendQueueMessage()

Manually send messages to a queue consumer.
import { env } from "cloudflare:test";
import worker from "./index";

it("should process queue messages", async () => {
  const messages = [
    { id: "1", timestamp: Date.now(), body: { action: "process" } },
    { id: "2", timestamp: Date.now(), body: { action: "delete" } },
  ];
  
  // Manually trigger queue handler
  await worker.queue?.({
    queue: "my-queue",
    messages,
    retryAll: () => {},
    ackAll: () => {},
  });
});

Mock Utilities

Custom Request Mocking

import { fetchMock, SELF } from "cloudflare:test";
import { it, expect, beforeAll, afterEach } from "vitest";

beforeAll(() => {
  fetchMock.activate();
});

afterEach(() => {
  fetchMock.assertNoPendingInterceptors();
});

it("should mock API responses", async () => {
  // Mock with exact match
  fetchMock
    .get("https://api.example.com")
    .intercept({ path: "/users/1", method: "GET" })
    .reply(200, { id: 1, name: "Alice" });

  // Mock with regex
  fetchMock
    .get("https://api.example.com")
    .intercept({ path: /\/users\/\d+/ })
    .reply(200, { id: 2, name: "Bob" });

  // Mock with function
  fetchMock
    .post("https://api.example.com")
    .intercept({ path: "/users" })
    .reply(201, async (opts) => {
      const body = await opts.body.json();
      return { ...body, id: 3 };
    });

  // Mock with headers
  fetchMock
    .get("https://api.example.com")
    .intercept({ path: "/auth", headers: { Authorization: "Bearer token" } })
    .reply(200, { authenticated: true });
});

Request Helpers

import { SELF } from "cloudflare:test";
import { it, expect } from "vitest";

function createRequest(path: string, options?: RequestInit): Request {
  return new Request(`https://example.com${path}`, options);
}

function createJsonRequest(path: string, body: unknown): Request {
  return createRequest(path, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
}

it("should handle JSON requests", async () => {
  const request = createJsonRequest("/api/users", { name: "Alice" });
  const response = await SELF.fetch(request);
  expect(response.status).toBe(201);
});

Time Utilities

advanceTime()

Advance time for testing scheduled events and timers.
import { vi, it, expect } from "vitest";

it("should handle timeouts", async () => {
  const start = Date.now();
  
  vi.useFakeTimers();
  
  const promise = new Promise((resolve) => {
    setTimeout(() => resolve("done"), 1000);
  });
  
  // Advance time by 1 second
  vi.advanceTimersByTime(1000);
  
  const result = await promise;
  expect(result).toBe("done");
  
  vi.useRealTimers();
});

Environment Utilities

setupTestEnvironment()

Set up a test environment with common bindings.
import { env } from "cloudflare:test";
import { beforeAll, afterAll } from "vitest";

beforeAll(async () => {
  // Initialize test data
  await env.DB.exec(`
    CREATE TABLE IF NOT EXISTS users (
      id INTEGER PRIMARY KEY,
      name TEXT NOT NULL
    );
  `);
  
  await env.MY_KV.put("config", JSON.stringify({
    apiUrl: "https://api.test.example.com",
  }));
});

afterAll(async () => {
  // Clean up
  await env.DB.exec("DROP TABLE IF EXISTS users");
  await env.MY_KV.delete("config");
});

Assertion Helpers

Response Assertions

import { expect } from "vitest";

async function expectJsonResponse(
  response: Response,
  status: number,
  body?: unknown
) {
  expect(response.status).toBe(status);
  expect(response.headers.get("content-type")).toContain("application/json");
  
  if (body !== undefined) {
    const json = await response.json();
    expect(json).toEqual(body);
  }
}

async function expectTextResponse(
  response: Response,
  status: number,
  text?: string
) {
  expect(response.status).toBe(status);
  
  if (text !== undefined) {
    const content = await response.text();
    expect(content).toBe(text);
  }
}

it("should return JSON", async () => {
  const response = await SELF.fetch("https://example.com/api/user/1");
  await expectJsonResponse(response, 200, { id: 1, name: "Alice" });
});

Complete Testing Example

// worker.test.ts
import {
  env,
  SELF,
  fetchMock,
  createExecutionContext,
  waitOnExecutionContext,
  runInDurableObject,
  getQueueResult,
} from "cloudflare:test";
import { describe, it, expect, beforeAll, beforeEach, afterEach } from "vitest";
import worker from "./index";

describe("Complete Worker Test Suite", () => {
  beforeAll(async () => {
    // Set up database schema
    await env.DB.exec(`
      CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        email TEXT UNIQUE NOT NULL
      );
    `);
    
    // Activate fetch mocking
    fetchMock.activate();
  });

  beforeEach(async () => {
    // Clean up before each test
    await env.DB.exec("DELETE FROM users");
    await env.MY_KV.list().then(({ keys }) =>
      Promise.all(keys.map((k) => env.MY_KV.delete(k.name)))
    );
  });

  afterEach(() => {
    fetchMock.assertNoPendingInterceptors();
  });

  it("should create user", async () => {
    const response = await SELF.fetch("https://example.com/api/users", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ name: "Alice", email: "alice@example.com" }),
    });

    expect(response.status).toBe(201);
    const user = await response.json();
    expect(user).toMatchObject({ name: "Alice", email: "alice@example.com" });

    // Verify in database
    const { results } = await env.DB.prepare("SELECT * FROM users").all();
    expect(results).toHaveLength(1);
  });

  it("should cache responses", async () => {
    const key = "cache-key";
    const value = { data: "cached" };

    await env.MY_KV.put(key, JSON.stringify(value));

    const response = await SELF.fetch(`https://example.com/cached?key=${key}`);
    expect(await response.json()).toEqual(value);
  });

  it("should use Durable Objects", async () => {
    const id = env.COUNTER.idFromName("global");

    // Increment counter
    await runInDurableObject(env.COUNTER, id, async (instance, state) => {
      const count = (await state.storage.get("count")) || 0;
      await state.storage.put("count", count + 1);
    });

    // Verify count
    await runInDurableObject(env.COUNTER, id, async (instance, state) => {
      const count = await state.storage.get("count");
      expect(count).toBe(1);
    });
  });

  it("should send queue messages", async () => {
    await SELF.fetch("https://example.com/trigger-task");

    const result = await getQueueResult(env.MY_QUEUE);
    expect(result.messages).toHaveLength(1);
    expect(result.messages[0].body).toMatchObject({ task: "process" });
  });

  it("should mock external APIs", async () => {
    fetchMock
      .get("https://api.external.com")
      .intercept({ path: "/data" })
      .reply(200, { value: 42 });

    const response = await SELF.fetch("https://example.com/external-data");
    const data = await response.json();
    expect(data.value).toBe(42);
  });
});

Build docs developers (and LLMs) love