Skip to content
Cloudflare Docs

This guide will instruct you through getting started with the @cloudflare/vitest-pool-workers package. For more complex examples of testing using @cloudflare/vitest-pool-workers, refer to Recipes.

Prerequisites

First, make sure that:

  • Your compatibility date is set to 2022-10-31 or later.

  • Your Worker using the ES modules format (if not, refer to the migrate to the ES modules format guide).

  • Vitest and @cloudflare/vitest-pool-workers are installed in your project as dev dependencies

    Terminal window
    npm i -D vitest@~3.0.0 @cloudflare/vitest-pool-workers

Define Vitest configuration

In your vitest.config.ts file, use defineWorkersConfig to configure the Workers Vitest integration.

You can use your Worker configuration from your Wrangler config file by specifying it with wrangler.configPath.

vitest.config.ts
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
export default defineWorkersConfig({
test: {
poolOptions: {
workers: {
wrangler: { configPath: "./wrangler.toml" },
},
},
},
});

You can also override or define additional configuration using the miniflare key. This takes precedence over values set in via your Wrangler config.

For example, this configuration would add a KV namespace TEST_NAMESPACE that was only accessed and modified in tests.

export default defineWorkersConfig({
test: {
poolOptions: {
workers: {
wrangler: { configPath: "./wrangler.toml" },
miniflare: {
kvNamespaces: ["TEST_NAMESPACE"],
},
},
},
},
});

For a full list of available Miniflare options, refer to the Miniflare WorkersOptions API documentation.

For a full list of available configuration options, refer to Configuration.

Define types

If you are not using Typescript, you can skip this section.

First make sure you have run wrangler types, which generates types for the Cloudflare Workers runtime and an Env type based on your Worker's bindings.

Then add a tsconfig.json in your tests folder and add "@cloudflare/vitest-pool-workers" to your types array to define types for cloudflare:test. You should also add the output of wrangler types to the include array so that the types for the Cloudflare Workers runtime are available.

Example test/tsconfig.json

test/tsconfig.json
{
"extends": "../tsconfig.json",
"compilerOptions": {
"moduleResolution": "bundler",
"types": [
"@cloudflare/vitest-pool-workers", // provides `cloudflare:test` types
],
},
"include": [
"./**/*.ts",
"../src/worker-configuration.d.ts", // output of `wrangler types`
],
}

You also need to define the type of the env object that is provided to your tests. Create an env.d.ts file in your tests folder, and declare the ProvidedEnv interface by extending the Env interface that is generated by wrangler types.

test/env.d.ts
declare module "cloudflare:test" {
// ProvidedEnv controls the type of `import("cloudflare:test").env`
interface ProvidedEnv extends Env {}
}

If your test bindings differ from the bindings in your Wrangler config, you should type them here in ProvidedEnv.

Writing tests

We will use this simple Worker as an example. It returns a 404 response for the /404 path and "Hello World!" for all other paths.

src/index.js
export default {
async fetch(request, env, ctx) {
if (pathname === "/404") {
return new Response("Not found", { status: 404 });
}
return new Response("Hello World!");
},
};

Unit tests

By importing the Worker we can write a unit test for its fetch handler.

test/unit.spec.js
import {
env,
createExecutionContext,
waitOnExecutionContext,
} from "cloudflare:test";
import { describe, it, expect } from "vitest";
// Import your worker so you can unit test it
import worker from "../src";
// For now, you'll need to do something like this to get a correctly-typed
// `Request` to pass to `worker.fetch()`.
const IncomingRequest = Request;
describe("Hello World worker", () => {
it("responds with Hello World!", async () => {
const request = new IncomingRequest("http://example.com/404");
// Create an empty context to pass to `worker.fetch()`
const ctx = createExecutionContext();
const response = await worker.fetch(request, env, ctx);
// Wait for all `Promise`s passed to `ctx.waitUntil()` to settle before running test assertions
await waitOnExecutionContext(ctx);
expect(await response.status).toBe(404);
expect(await response.text()).toBe("Not found");
});
});

Integration tests

You can use the SELF fetcher provided by the cloudflare:test to write an integration test. This is a service binding to the default export defined in the main Worker.

test/integration.spec.js
import { SELF } from "cloudflare:test";
import { describe, it, expect } from "vitest";
describe("Hello World worker", () => {
it("responds with not found and proper status for /404", async () => {
const response = await SELF.fetch("http://example.com/404");
expect(await response.status).toBe(404);
expect(await response.text()).toBe("Not found");
});
});

When using SELF for integration tests, your Worker code runs in the same context as the test runner. This means you can use global mocks to control your Worker, but also means your Worker uses the subtly different module resolution behavior provided by Vite. Usually this is not a problem, but to run your Worker in a fresh environment that is as close to production as possible, you can use an auxiliary Worker. Refer to this example for how to set up integration tests using auxiliary Workers. However, using auxiliary Workers comes with limitations that you should be aware of.