Write your first test
This guide will instruct you through installing and setting up the @cloudflare/vitest-pool-workers
package. This will help you get started writing tests against your Workers using Vitest. The @cloudflare/vitest-pool-workers
package works by running code inside a Cloudflare Worker that Vitest would usually run inside a Node.js worker thread ↗. For examples of tests using @cloudflare/vitest-pool-workers
, refer to Recipes.
-
Open the root directory of your Worker or create a new Worker.
-
Make sure that your Worker is developed using the ES modules format. To migrate from the service worker format to the ES modules format, refer to the Migrate to the ES modules format guide.
-
In your project's
wrangler.toml / wrangler.json
file, define a compatibility date of2022-10-31
or higher, and includenodejs_compat
in your compatibility flags.
Open a terminal window and make sure you are in your project's root directory. Once you have confirmed that, run:
npm install vitest@2.1.8 --save-dev --save-exactnpm install @cloudflare/vitest-pool-workers --save-dev
The above commands will add the packages to your package.json
file and install them as dev dependencies.
If you do not already have a vitest.config.js
or vitest.config.ts
file, you will need to create one and define the following configuration.
You can reference a Wrangler file to leverage its main
entry point, compatibility settings, and bindings.
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
export default defineWorkersConfig({ test: { poolOptions: { workers: { wrangler: { configPath: "./wrangler.toml" }, }, }, },});
Under the hood, the Workers Vitest integration uses Miniflare ↗, the same simulator that powers wrangler dev
's local mode. Options can be passed directly to Miniflare for advanced configuration.
For example, to add bindings that will be used in tests, you can add miniflare
to defineWorkersConfig
:
export default defineWorkersConfig({ test: { poolOptions: { workers: { main: "./src/index.ts", miniflare: { kvNamespaces: ["TEST_NAMESPACE"], }, }, }, },});
This configuration would add a KV namespace TEST_NAMESPACE
that was only accessible in tests. Using this method, you can add or override existing bindings like Durable Objects or service bindings.
If you are using TypeScript, you will need to define types for Cloudflare Workers and cloudflare:test
to make sure they are detected appropriately. Add a tsconfig.json
in the same folder as your tests (that is, test
) and add the following:
{ "extends": "../tsconfig.json", "compilerOptions": { "moduleResolution": "bundler", "types": [ "@cloudflare/workers-types/experimental", "@cloudflare/vitest-pool-workers" ] }, "include": ["./**/*.ts", "../src/env.d.ts"]}
Save this file, and you are ready to write your first test.
If you created a basic Worker via the guide listed above, you should have the following fetch handler in the src
folder:
export default { async fetch(request, env, ctx) { return new Response("Hello World!"); },};
export default { async fetch(request, env, ctx): Promise<Response> { return new Response("Hello World!"); },} satisfies ExportedHandler<Env>;
This Worker receives a request, and returns a response of "Hello World!"
. In order to test this, create a test
folder with the following test file:
import { env, createExecutionContext, waitOnExecutionContext,} from "cloudflare:test";import { describe, it, expect } from "vitest";// Could import any other source file/function hereimport worker from "../src";
describe("Hello World worker", () => { it("responds with Hello World!", async () => { const request = new Request("http://example.com"); // 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.text()).toBe("Hello World!"); });});
import { env, createExecutionContext, waitOnExecutionContext,} from "cloudflare:test";import { describe, it, expect } from "vitest";// Could import any other source file/function hereimport 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"); // 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.text()).toBe("Hello World!"); });});
Add functionality to handle a 404
path on the Worker. This functionality will return the text Not found
as well as the status code 404
.
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!"); },};
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>;
To test this, add the following to your test file:
it("responds with not found and proper status for /404", async () => { const request = new Request("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");});
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(await response.status).toBe(404); expect(await response.text()).toBe("Not found");});
@cloudflare/vitest-pool-workers
GitHub repository ↗ - Examples of tests using the@cloudflare/vitest-pool-workers
package.