Write your first test
Last updated Oct 17, 2026
This guide explains how to get started with the @cloudflare/vitest-plugin package. For more complex examples of testing with @cloudflare/vitest-plugin, refer to Recipes.
Prerequisites
First, make sure that:
- Your compatibility date is set to
2022-10-31or later. - Your Worker using the ES modules format (if not, refer to the migrate to the ES modules format guide).
- Vitest and
@cloudflare/vitest-pluginare installed in your project as dev dependenciesnpmyarnpnpmbun
npm i -D vitest@^4.1.0 @cloudflare/vitest-plugin
yarn add -D vitest@^4.1.0 @cloudflare/vitest-plugin
pnpm add -D vitest@^4.1.0 @cloudflare/vitest-plugin
bun add -d vitest@^4.1.0 @cloudflare/vitest-plugin
Note
The @cloudflare/vitest-plugin package requires Vitest 4.1 or later.
Define Vitest configuration
In your vitest.config.ts file, use the cloudflareTest() plugin to configure the Workers Vitest integration.
You can use your Worker configuration from your Wrangler config file by specifying it with wrangler.configPath.
import { cloudflareTest } from "@cloudflare/vitest-plugin";
import { defineConfig } from "vitest/config";
export default defineConfig({
plugins: [
cloudflareTest({
wrangler: { configPath: "./wrangler.jsonc" },
}),
],
});
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 defineConfig({
plugins: [
cloudflareTest({
wrangler: { configPath: "./wrangler.jsonc" },
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-plugin/types" 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.
<details>
<summary>
Example test/tsconfig.json
</summary>
test/tsconfig.jsonjsonc
{
"extends": "../tsconfig.json",
"compilerOptions": {
"moduleResolution": "bundler",
"types": [
"@cloudflare/vitest-plugin/types", // provides `cloudflare:test` and `cloudflare:workers` types
],
},
"include": [
"./**/*.ts",
"../src/worker-configuration.d.ts", // output of `wrangler types`
],
}
</details>
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.jsjs
export default {
async fetch(request, env, ctx) {
const { pathname } = new URL(request.url);
if (pathname === "/404") {
return new Response("Not found", { status: 404 });
}
return new Response("Hello World!");
},
};
src/index.tsts
export default {
async fetch(request, env, ctx): Promise<Response> {
const { pathname } = new URL(request.url);
if (pathname === "/404") {
return new Response("Not found", { status: 404 });
}
return new Response("Hello World!");
},
} satisfies ExportedHandler<Env>;
Unit tests
By importing the Worker we can write a unit test for its fetch handler.
test/unit.spec.jsjs
import { env } from "cloudflare:workers";
import {
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 not found and proper status for /404", 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(response.status).toBe(404);
expect(await response.text()).toBe("Not found");
});
});
test/unit.spec.tsts
import { env } from "cloudflare:workers";
import {
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<unknown, IncomingRequestCfProperties>;
describe("Hello World worker", () => {
it("responds with not found and proper status for /404", 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(response.status).toBe(404);
expect(await response.text()).toBe("Not found");
});
});
Integration tests
You can use the exports object provided by cloudflare:workers to write an integration test. exports.default.fetch() calls the default export handler defined in the main Worker.
test/integration.spec.jsjs
import { exports } from "cloudflare:workers";
import { describe, it, expect } from "vitest";
describe("Hello World worker", () => {
it("responds with not found and proper status for /404", async () => {
const response = await exports.default.fetch("http://example.com/404");
expect(response.status).toBe(404);
expect(await response.text()).toBe("Not found");
});
});
test/integration.spec.tsts
import { exports } from "cloudflare:workers";
import { describe, it, expect } from "vitest";
describe("Hello World worker", () => {
it("responds with not found and proper status for /404", async () => {
const response = await exports.default.fetch("http://example.com/404");
expect(response.status).toBe(404);
expect(await response.text()).toBe("Not found");
});
});
When using exports.default.fetch() 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.
Related resources
- For more complex examples of testing with
@cloudflare/vitest-plugin, refer to Recipes. - Configuration API reference
- Test APIs reference
Was this helpful?
YesNo
On this page
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/workers/testing/vitest-integration/write-your-first-test/#page","headline":"Write your first test","description":"Write tests against Workers using Vitest","url":"https://developers.cloudflare.com/workers/testing/vitest-integration/write-your-first-test/","inLanguage":"en","image":"https://developers.cloudflare.com/workers/testing/vitest-integration/write-your-first-test/og.png?v=4f540ef29b4b992d","dateModified":"2026-10-17","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
description: Wrangler is the CLI for the Cloudflare Developer Platform. Use it to build, test, and deploy Workers projects. title: Wrangler image: https://developers.cloudflare.com/workers/wrangler/og.png?v=c466a5d29c4457f9
Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/workers/llms.txt
Use this file to discover all available pages before exploring further.
Docs