Module cache in Workers Vitest integration
Each Worker has its own module cache. As Workers are reused between test runs, their module caches are also reused. Vitest invalidates parts of the module cache at the start of each test run based on changed files.
Compatibility flags automatically injected by Vitest Pool Workers
The Workers Vitest pool automatically injects the nodejs_compat, no_nodejs_compat_v2, and export_commonjs_default compatibility flags. This is the minimal compatibility setup that allows Vitest to run correctly without pulling in polyfills and globals that are not required. If you already have a Node.js compatibility flag defined in your configuration, Vitest Pool Workers will not try to add those flags.
Importing Node.js modules without nodejs_compat flag
If you do not have a nodejs_compat or nodejs_compat_v2 flag in your configuration and you import a Node.js module in your Worker code, your tests may pass, but you will not be able to deploy the Worker. The upload call (either via the REST API or via Wrangler) will throw an error.
Using Node.js globals without nodejs_compat flag
If you use Node.js globals that are not supported by the runtime without specifying a nodejs_compat flag, your Worker upload will be successful, but you may see errors in production code. The tests will pass during local testing, but the Worker will fail at runtime when the Node.js global is accessed in production.
V8 code coverage not supported in Workers Vitest
Native code coverage via V8 is not supported in the Workers Vitest pool. You must use instrumented code coverage via Istanbul instead. Refer to Vitest Coverage documentation for setup instructions.
Use using keyword for RPC methods returning non-primitive values
When calling RPC methods of a Service Worker or Durable Object that return non-primitive values such as objects or classes extending RpcTarget, use the using keyword to explicitly signal when resources can be disposed of.
Consume response bodies in tests
When making requests via fetch or R2.get(), consume the entire response body, even if you are not asserting its content. For example, call await response.text() to ensure the response body is consumed.
Example: Consume R2 response body in test
test("check if file exists", async () => {
await env.R2.put("file", "hello-world");
const response = await env.R2.get("file");
expect(response).not.toBe(null);
// Consume the response body even if you are not asserting it
await response.text();
});
ctx.exports missing properties with complex build setups
The ctx.exports property provides access to the exports of the main Worker. The Workers Vitest integration attempts to automatically infer these exports by statically analyzing the Worker source code using esbuild. However, complex build setups such as those using virtual modules or wildcard re-exports that esbuild cannot follow may result in missing properties on the ctx.exports object.
Always await storage operations in tests
Always await all Promises that read or write to storage services like KV and R2. For example, in beforeAll hooks, use await env.KV.put() and await env.R2.put() rather than running them without awaiting.
Storage isolation is per test file
Storage isolation is per test file. The test runner will undo any writes to storage at the end of each test file. This means storage changes made during a test are rolled back after the test file completes.
WebSockets with Durable Objects not supported with per-file storage isolation
Using WebSockets with Durable Objects is not supported with per-file storage isolation. To work around this, run your tests with shared storage using --max-workers=1 --no-isolate.
Dynamic import statements with exports and Durable Objects
Dynamic import() statements do not work inside export default { ... } handlers when writing integration tests with exports.default.fetch(), or inside Durable Object event handlers. You must import and call your handlers directly, or use static import statements in the global scope.
Fake timers do not apply to KV, R2 and cache simulators
Vitest's fake timers do not apply to KV, R2 and cache simulators. For example, you cannot expire a KV key by advancing fake time.
additionalExports option in Vitest configuration
To work around missing properties on ctx.exports caused by complex build setups, add the additionalExports option to the cloudflareTest plugin configuration. This option is a map where keys are the export names and values are the type of export ("WorkerEntrypoint", "DurableObject", or "WorkflowEntrypoint").
Example: additionalExports configuration for virtual modules
import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
import { defineConfig } from "vitest/config";
export default defineConfig({
plugins: [
cloudflareTest({
wrangler: { configPath: "./wrangler.jsonc" },
additionalExports: {
MyEntrypoint: "WorkerEntrypoint",
},
}),
],
});
Module resolution issues with require and ES modules
If you encounter module resolution issues such as "Error: Cannot use require() to import an ES Module" or "Error: No such module", you can bundle these dependencies using the deps.optimizer option in Vitest configuration.
Example: deps.optimizer configuration for module resolution
import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
import { defineConfig } from "vitest/config";
export default defineConfig({
plugins: [
cloudflareTest({
// ...
}),
],
test: {
deps: {
optimizer: {
ssr: {
enabled: true,
include: ["your-package-name"],
},
},
},
},
});
Global setup file runs in Node.js environment
Although Vitest is set up to resolve packages for the workerd runtime, it runs your global setup file in the Node.js environment. This can cause issues when importing packages that export a non-Node version for workerd.
Example: Wrapper for global setup file with Vite SSR import
// File: global-setup-wrapper.ts
import { createServer } from "vite";
// Import the actual global setup file with the correct setup
const mod = await viteImport("./global-setup.ts");
export default mod.default;
// Helper to import the file with default node setup
async function viteImport(file: string) {
const server = await createServer({
root: import.meta.dirname,
configFile: false,
server: { middlewareMode: true, hmr: false, watch: null, ws: false },
optimizeDeps: { noDiscovery: true },
clearScreen: false,
});
const mod = await server.ssrLoadModule(file);
await server.close();
return mod;
}
Example: Using global-setup-wrapper in Vitest configuration
// File: vitest.config.ts
import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
import { defineConfig } from "vitest/config";
export default defineConfig({
plugins: [
cloudflareTest({
// ...
}),
],
test: {
// Replace the globalSetup with the wrapper file
globalSetup: ["./global-setup-wrapper.ts"],
},
});
Example: Seed data in beforeAll hook
beforeAll(async () => {
await env.KV.put("message", "test message");
await env.R2.put("file", "hello-world");
});
Example: Use RPC method with using keyword
using result = await stub.getCounter();
Install Workers Vitest integration
To migrate from vitest-environment-miniflare to the new pool: uninstall vitest-environment-miniflare, install vitest@^4.1.0 or later, and install @cloudflare/vitest-pool-workers. Vitest environments can only customize the global scope, whereas pools run tests using a completely different runtime (workerd instead of Node.js).
Update TypeScript types for Workers Vitest integration
In tsconfig.json, replace vitest-environment-miniflare/globals with @cloudflare/vitest-pool-workers/types in the types array under compilerOptions.
Access bindings in Workers Vitest tests
To access bindings in tests, use the env helper from the cloudflare:workers module instead of getMiniflareBindings(). If using TypeScript, define the type of env for your tests.
Mock outbound requests in Workers Vitest integration
The getMiniflareFetchMock() function is no longer available. To mock outbound fetch() requests, mock globalThis.fetch directly or use ecosystem libraries such as MSW (Mock Service Worker).
Storage isolation in Workers Vitest integration
Storage isolation is per test file by default in the Workers Vitest integration. You no longer need to include setupMiniflareIsolatedStorage() in your tests. Use the standard describe function from vitest instead.
Replace ExecutionContext API for Workers Vitest integration
Replace new ExecutionContext() with createExecutionContext() and getMiniflareWaitUntil() with waitOnExecutionContext(). Both are imported from cloudflare:test. Note that waitOnExecutionContext() returns Promise<void> instead of a Promise resolving to the results of all waitUntil()ed Promises.
Replace getMiniflareDurableObjectIds with listDurableObjectIds
The getMiniflareDurableObjectIds() function has been replaced with listDurableObjectIds() from cloudflare:test. The listDurableObjectIds() function accepts a DurableObjectNamespace instance instead of a namespace string for stricter typing. The function respects storage isolation, so IDs of objects created in other test files will not be returned.
Replace flushMiniflareDurableObjectAlarms with runDurableObjectAlarm
The flushMiniflareDurableObjectAlarms() function has been replaced with runDurableObjectAlarm() from cloudflare:test. The runDurableObjectAlarm() function accepts a single DurableObjectStub and returns a Promise that resolves to true if an alarm was scheduled and executed, or false otherwise. To flush multiple instances' alarms, call runDurableObjectAlarm() in a loop.
Replace Durable Object helper functions with runInDurableObject
The getMiniflareDurableObjectStorage(), getMiniflareDurableObjectState(), getMiniflareDurableObjectInstance(), and runWithMiniflareDurableObjectGates() functions have been replaced with a single runInDurableObject() function from cloudflare:test. The runInDurableObject() function accepts a DurableObjectStub with a callback accepting the Durable Object instance and DurableObjectState as arguments. This simplifies the API and ensures instances are accessed with correct request context and gating behavior.
Migrate from Miniflare 2 to Workers Vitest integration
Miniflare 2 provided custom Jest and Vitest environments. The @cloudflare/vitest-pool-workers package provides similar functionality using modern Miniflare versions and the workerd runtime. workerd is the same JavaScript/WebAssembly runtime that powers Cloudflare Workers, which practically eliminates behavior mismatches between tests and deployed code.
Jest testing environment for Workers no longer supported
Cloudflare no longer provides a Jest testing environment for Workers. If you previously used Jest, you must migrate to Vitest first before using the Workers Vitest integration. Vitest provides built-in support for TypeScript, ES modules, and hot-module reloading for tests.
Update Vitest configuration for cloudflareTest plugin
Update your Vitest configuration file to use the cloudflareTest() Vite plugin instead of the miniflare environment. Move most Miniflare configuration from environmentOptions to the miniflare option in cloudflareTest(). If relying on Wrangler configuration, set wrangler.configPath to your Wrangler file path. Use defineConfig from vitest/config instead of defineWorkersConfig, and add cloudflareTest to the plugins array.
unstable_dev to Vitest integration migration overview
The unstable_dev API has been a recommended approach to run integration tests. The @cloudflare/vitest-pool-workers package integrates directly with Vitest for fast re-runs, supports both unit and integration tests, and provides isolated per-test storage. Cloudflare recommends using the createTestHarness() API for integration testing.
Import Worker for integration testing with Vitest
With the Workers Vitest integration, you reference a Worker using exports from cloudflare:workers. exports.default refers to the default export defined by the main option in the Wrangler configuration file. The main Worker runs in the same isolate as tests so any global mocks will apply to it too. You must also import the main file with import '../src/' to automatically rerun tests when main changes.
Example: trigger fetch event with Vitest integration
To trigger a fetch event with the Workers Vitest integration:
```js
import { exports } from "cloudflare:workers";
import "../src/"; // Currently required to automatically rerun tests when `main` changes
it("dispatches fetch event", async () => {
const response = await exports.default.fetch("http://example.com");
...
});
```
Worker lifecycle management in Vitest integration
With the Workers Vitest integration, there is no need to stop a Worker via worker.stop(). This functionality is handled automatically after tests run.
Configure Wrangler configuration file in Vitest
With the Workers Vitest integration, set a reference to a Wrangler configuration file in vitest.config.js for all tests using the configPath option in the cloudflareTest plugin configuration.
Example: Vitest configuration with Wrangler config path
To reference a Wrangler configuration file in the Workers Vitest integration:
```js
import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
import { defineConfig } from "vitest/config";
export default defineConfig({
plugins: [
cloudflareTest({
wrangler: {
configPath: "wrangler.jsonc",
},
}),
],
});
```
Service Worker format not supported in Vitest integration
The Workers Vitest integration does not support testing Workers using the service worker format. You must migrate to the ES modules format in order to use the Workers Vitest integration.
Remove UnstableDevWorker type imports
When migrating from unstable_dev to the Workers Vitest integration, you can remove UnstableDevWorker imports from your code. Instead, follow the Write your first test guide to define types for all tests.
vitest-pool-workers v0.13.0 rearchitects around Vite plugin model
Version 0.13.0 rearchitects the integration around a Vite plugin model, which breaks the configuration API but resolves multiple issues: library imports like Stripe no longer require SSR optimizer workarounds, bare Node.js specifiers such as node:url resolve in test files, nodejs_compat_v2 and Node.js module flags are enabled automatically during tests matching production behavior, the provide data channel uses WebSocket messages instead of being limited to ~8 KB, storage is isolated per test file instead of per test, and the Vitest UI works correctly with Workers tests.
Replace fetchMock with direct fetch mocking
The import { fetchMock } from "cloudflare:test" import has been removed. Mock globalThis.fetch directly or use ecosystem libraries such as MSW (https://mswjs.io/).
Replace cloudflare:test env and SELF imports with cloudflare:workers
The env and SELF exports from cloudflare:test are deprecated in favor of cloudflare:workers. Replace import { env, SELF } from "cloudflare:test" with import { env, exports } from "cloudflare:workers". exports.default.fetch() behaves the same as SELF.fetch() except it does not expose Assets. To test Assets, use the env.ASSETS binding or write an integration test using startDevWorker(). The deprecated exports still work, so this change is recommended rather than required.
Remove isolatedStorage and singleWorker options
The isolatedStorage and singleWorker options have been removed. Storage isolation is now per test file, matching Vitest's own isolation model. If these options were previously set, remove them from the cloudflareTest() call. To make test files share the same storage instead, pass --max-workers=1 --no-isolate flags to the Vitest command in package.json.
@cloudflare/vitest-pool-workers v0.13.0 requires Vitest 4
@cloudflare/vitest-pool-workers v0.13.0 adds support for Vitest 4. v0.12.x is the last version to support Vitest 3.x. To migrate, install vitest@^4.1.0 and @cloudflare/vitest-pool-workers. Both @vitest/runner and @vitest/snapshot at ^4.1.0 are required as dependencies, which ship with vitest.
Run codemod to migrate vitest.config.ts automatically
Run the codemod with: npx jscodeshift -t node_modules/@cloudflare/vitest-pool-workers/dist/codemods/vitest-v3-to-v4.mjs vitest.config.ts after installing the package. To run without installing first, use: npx jscodeshift -t https://unpkg.com/@cloudflare/vitest-pool-workers/dist/codemods/vitest-v3-to-v4.mjs --parser=ts vitest.config.ts. The codemod only updates vitest.config.ts and does not remove unsupported options. Changes to test files, including cloudflare:test import updates, must be made manually.
Replace defineWorkersProject and defineWorkersConfig with cloudflareTest() plugin
defineWorkersProject and defineWorkersConfig from @cloudflare/vitest-pool-workers/config have been removed and replaced by a cloudflareTest() Vite plugin exported from @cloudflare/vitest-pool-workers. Options previously nested under test.poolOptions.workers are passed directly to cloudflareTest(). A codemod is available to migrate configurations that use defineWorkersProject with an object. Manual migration is required for configurations using defineWorkersConfig or calling defineWorkersProject with a function.
Configuration migration example from v0.12.x to v0.13.x
Before: import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config"; export default defineWorkersProject({ test: { poolOptions: { workers: { wrangler: { configPath: "./wrangler.jsonc" } } } } });
After: import { cloudflareTest } from "@cloudflare/vitest-pool-workers"; import { defineConfig } from "vitest/config"; export default defineConfig({ plugins: [ cloudflareTest({ wrangler: { configPath: "./wrangler.jsonc" } }) ] });
Vitest integration supports module resolution with Vite Dependency Pre-Bundling
The integration allows resolving modules with Vite Dependency Pre-Bundling to handle module resolution challenges.
Vitest integration supports testing Workers Assets
Test recipes are available for mocking and testing Workers Assets.
Vitest integration supports testing Hyperdrive with TCP server
Tests can be written using Hyperdrive with a Vitest managed TCP server for database connection testing.
Vitest integration supports testing Pipelines
Test recipes are available for testing Pipelines.
@cloudflare/vitest-pool-workers package for testing Workers
The @cloudflare/vitest-pool-workers package enables unit and integration testing for Cloudflare Workers projects using Vitest.
Vitest integration supports testing with KV, R2, D1, Durable Objects, and Cache API
The @cloudflare/vitest-pool-workers integration provides examples and support for writing tests that use KV, R2, D1 with migrations, Durable Objects with direct access, and the Cache API.
Vitest integration supports testing Queues and Workflows
Test recipes are available for writing tests with Queue producers and consumers, as well as for Workflows.
Vitest integration supports testing with auxiliary Workers and request mocking
The integration supports integration tests using auxiliary Workers, request mocking (both declarative with MSW and imperative), and multiple auxiliary Workers with request mocks.
Vitest integration supports testing Pages Functions
Basic unit and integration tests can be written for Pages Functions using exports.default.