Write your first test
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.
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 dependenciesTerminal window npm i -D vitest@~3.0.0 @cloudflare/vitest-pool-workersTerminal window pnpm add -D vitest@~3.0.0 @cloudflare/vitest-pool-workersTerminal window yarn add -D vitest@~3.0.0 @cloudflare/vitest-pool-workers
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
.
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.
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
{ "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
.
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
.
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.
export default { async fetch(request, env, ctx) { if (pathname === "/404") { return new Response("Not found", { status: 404 }); } return new Response("Hello World!"); },};
export default { async fetch(request, env, ctx): Promise<Response> { if (pathname === "/404") { return new Response("Not found", { status: 404 }); } return new Response("Hello World!"); },} satisfies ExportedHandler<Env>;
By importing the Worker we can write a unit test for its fetch
handler.
import { env, createExecutionContext, waitOnExecutionContext,} from "cloudflare:test";import { describe, it, expect } from "vitest";// Import your worker so you can unit test itimport 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"); });});
import { env, createExecutionContext, waitOnExecutionContext,} from "cloudflare:test";import { describe, it, expect } from "vitest";// Import your worker so you can unit test itimport 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<unknown, IncomingRequestCfProperties>;
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"); });});
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.
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"); });});
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.
- For more complex examples of testing using
@cloudflare/vitest-pool-workers
, refer to Recipes. - Configuration API reference
- Test APIs reference
Was this helpful?
- Resources
- API
- New to Cloudflare?
- Products
- Sponsorships
- Open Source
- Support
- Help Center
- System Status
- Compliance
- GDPR
- Company
- cloudflare.com
- Our team
- Careers
- 2025 Cloudflare, Inc.
- Privacy Policy
- Terms of Use
- Report Security Issues
- Trademark