Testing Cloudflare Workers with Vitest and Miniflare

Yuki Tanaka · · 11 views

Unit testing Workers used to mean mocking the entire runtime. The Workers Vitest pool gives you a real Workers runtime inside your test suite — no mocks required.

The Old Way: Mocking Fetch

Before the Workers Vitest pool, testing a Worker meant mocking fetch, Request, Response, and hoping your mocks matched the real behaviour. This led to tests that passed locally but broke in production.

Enter @cloudflare/vitest-pool-workers

The @cloudflare/vitest-pool-workers package runs your tests inside a real Workers runtime using Miniflare. You get access to D1, KV, R2, and all other bindings — backed by local in-memory implementations.

Setup

npm install -D vitest @cloudflare/vitest-pool-workers
// vitest.config.ts
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";

export default defineWorkersConfig({
  test: {
    poolOptions: {
      workers: {
        wrangler: { configPath: "./wrangler.jsonc" }
      }
    }
  }
});

Writing a Test

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

beforeAll(async () => {
  await env.DB.exec(`INSERT INTO articles (slug, title) VALUES ('test', 'Test')`);
});

describe("GET /api/articles", () => {
  it("returns a list of articles", async () => {
    const response = await SELF.fetch("http://localhost/api/articles", {
      headers: { Authorization: "Bearer test-token" }
    });
    expect(response.status).toBe(200);
    const body = await response.json();
    expect(body.articles).toBeInstanceOf(Array);
  });
});

Tips

  • Use env.DB.batch() in beforeAll / afterEach to set up and tear down fixtures
  • SELF.fetch() exercises the real routing layer — prefer it over unit-testing handlers in isolation
  • Miniflare resets storage between test files but not between tests in the same file