Test APIs

Last updated Oct 10, 2026 The Workers Vitest integration provides runtime helpers for writing tests. Some helpers are exported from the cloudflare:workers module, and others from the cloudflare:test module. Both modules are provided by the @cloudflare/vitest-plugin package, but can only be imported from test files that execute in the Workers runtime.

cloudflare:workers exports

  • env: import("cloudflare:workers").ProvidedEnv
  • Exposes the env object for use as the second argument passed to ES modules format exported handlers. This provides access to bindings that you have defined in your Vitest configuration file.

```js import { env } from "cloudflare:workers";

it("uses binding", async () => { await env.KV_NAMESPACE.put("key", "value"); expect(await env.KV_NAMESPACE.get("key")).toBe("value"); }); ```

To configure the type of this value, use an ambient module type:

ts declare module "cloudflare:workers" { interface ProvidedEnv { KV_NAMESPACE: KVNamespace; } // ...or if you have an existing `Env` type... interface ProvidedEnv extends Env {} }

  • exports: object
  • Provides access to the exports of the main Worker. Use exports.default.fetch() to write integration tests against your Worker's default export handler. The main Worker runs in the same isolate/context as tests so any global mocks will apply to it too. Unlike the previous SELF binding, exports does not expose Assets. To test assets, use startDevWorker().

```js import { exports } from "cloudflare:workers";

it("dispatches fetch event", async () => { const response = await exports.default.fetch("https://example.com"); expect(await response.text()).toMatchInlineSnapshot(...); }); ```

cloudflare:test exports

Events

  • createExecutionContext(): ExecutionContext
  • Creates an instance of the context object for use as the third argument to ES modules format exported handlers.
  • waitOnExecutionContext(ctx:ExecutionContext): Promise\<void>
  • Use this to wait for all Promises passed to ctx.waitUntil() to settle, before running test assertions on any side effects. Only accepts instances of ExecutionContext returned by createExecutionContext().

```ts import { env } from "cloudflare:workers"; import { createExecutionContext, waitOnExecutionContext } from "cloudflare:test"; import { it, expect } from "vitest"; import worker from "./index.mjs";

it("calls fetch handler", async () => { const request = new Request("https://example.com"); const ctx = createExecutionContext(); const response = await worker.fetch(request, env, ctx); await waitOnExecutionContext(ctx); expect(await response.text()).toMatchInlineSnapshot(...); }); ```

  • createScheduledController(options?:FetcherScheduledOptions): ScheduledController
  • Creates an instance of ScheduledController for use as the first argument to modules-format scheduled() exported handlers.

```ts import { env } from "cloudflare:workers"; import { createScheduledController, createExecutionContext, waitOnExecutionContext } from "cloudflare:test"; import { it, expect } from "vitest"; import worker from "./index.mjs";

it("calls scheduled handler", async () => { const ctrl = createScheduledController({ scheduledTime: new Date(1000), cron: "30 * * * *" }); const ctx = createExecutionContext(); await worker.scheduled(ctrl, env, ctx); await waitOnExecutionContext(ctx); }); ```

  • createMessageBatch(queueName:string, messages:ServiceBindingQueueMessage[]): MessageBatch
  • Creates an instance of MessageBatch for use as the first argument to modules-format queue() exported handlers.
  • getQueueResult(batch:MessageBatch, ctx:ExecutionContext): Promise\<FetcherQueueResult>
  • Gets the acknowledged/retry state of messages in the MessageBatch, and waits for all ExecutionContext#waitUntil()ed Promises to settle. Only accepts instances of MessageBatch returned by createMessageBatch(), and instances of ExecutionContext returned by createExecutionContext().

```ts import { env } from "cloudflare:workers"; import { createMessageBatch, createExecutionContext, getQueueResult } from "cloudflare:test"; import { it, expect } from "vitest"; import worker from "./index.mjs";

it("calls queue handler", async () => { const batch = createMessageBatch("my-queue", [ { id: "message-1", timestamp: new Date(1000), body: "body-1" } ]); const ctx = createExecutionContext(); await worker.queue(batch, env, ctx); const result = await getQueueResult(batch, ctx); expect(result.ackAll).toBe(false); expect(result.retryBatch).toMatchObject({ retry: false }); expect(result.explicitAcks).toStrictEqual(["message-1"]); expect(result.retryMessages).toStrictEqual([]); }); ```

Durable Objects

  • runInDurableObject<O extends DurableObject, R>(stub:DurableObjectStub, callback:(instance: O, state: DurableObjectState) => R | Promise<R>): Promise\<R>
  • Runs the provided callback inside the Durable Object that corresponds to the provided stub.

This temporarily replaces your Durable Object's fetch() handler with callback, then sends a request to it, returning the result. This can be used to call/spy-on Durable Object methods or seed/get persisted data. Note this can only be used with stubs pointing to Durable Objects defined in the main Worker.

```ts export class Counter { constructor(readonly state: DurableObjectState) {}

async fetch(request: Request): Promise { let count = (await this.state.storage.get("count")) ?? 0; void this.state.storage.put("count", ++count); return new Response(count.toString()); } } ```

```ts import { env } from "cloudflare:workers"; import { runInDurableObject } from "cloudflare:test"; import { it, expect } from "vitest"; import { Counter } from "./index.ts";

it("increments count", async () => { const id = env.COUNTER.newUniqueId(); const stub = env.COUNTER.get(id); let response = await stub.fetch("https://example.com"); expect(await response.text()).toBe("1");

response = await runInDurableObject(stub, async (instance: Counter, state) => { expect(instance).toBeInstanceOf(Counter); expect(await state.storage.get("count")).toBe(1);

const request = new Request("https://example.com"); return instance.fetch(request); }); expect(await response.text()).toBe("2"); }); ```

  • runDurableObjectAlarm(stub:DurableObjectStub): Promise\<boolean>
  • Immediately runs and removes the Durable Object pointed to by stub's alarm if one is scheduled. Returns true if an alarm ran, and false otherwise. Note this can only be used with stubs pointing to Durable Objects defined in the main Worker.
  • evictDurableObject(stub:DurableObjectStub, options?:DurableObjectEvictionOptions): Promise\<void>
  • Evicts the currently-running Durable Object pointed to by stub, tearing down its instance to reset in-memory state. By default, hibernatable WebSockets are hibernated rather than closed, and eviction waits up to 30 seconds for in-flight requests to drain.

Useful for testing how a Durable Object behaves across evictions, such as recovering state from storage or resuming hibernated WebSockets.

Rejects if stub is not a Durable Object stub, if the target Durable Object is not currently running, or if its namespace has eviction prevented. Note this can only be used with stubs pointing to Durable Objects defined in the main Worker.

```ts import { env } from "cloudflare:workers"; import { evictDurableObject } from "cloudflare:test"; import { it, expect } from "vitest";

it("preserves stored data across eviction", async () => { const id = env.COUNTER.idFromName("evict-test"); const stub = env.COUNTER.get(id);

// Each request increments and persists the count to storage expect(await (await stub.fetch("https://example.com")).text()).toBe("1"); expect(await (await stub.fetch("https://example.com")).text()).toBe("2");

// Evict the Durable Object. The in-memory instance is torn down, // but durable storage is preserved. await evictDurableObject(stub);

// The next request reconstructs the instance and reads the persisted count expect(await (await stub.fetch("https://example.com")).text()).toBe("3"); }); ```

  • The DurableObjectEvictionOptions interface controls eviction behavior:
Property Type Default Description
webSockets "close" \| "hibernate" "hibernate" Controls what happens to hibernatable WebSockets when evicting a Durable Object. With "hibernate", WebSockets are hibernated and can resume after eviction. With "close", WebSockets are closed.
- listDurableObjectIds(namespace:DurableObjectNamespace): Promise\<DurableObjectId[]>
- Gets the IDs of all objects that have been created in the namespace. Respects per-file storage isolation, meaning objects created in a different test file will not be returned.

```ts import { env } from "cloudflare:workers"; import { listDurableObjectIds } from "cloudflare:test"; import { it, expect } from "vitest";

it("increments count", async () => { const id = env.COUNTER.newUniqueId(); const stub = env.COUNTER.get(id); const response = await stub.fetch("https://example.com"); expect(await response.text()).toBe("1");

const ids = await listDurableObjectIds(env.COUNTER); expect(ids.length).toBe(1); expect(ids[0].equals(id)).toBe(true); }); ```

  • reset(): Promise\<void>
  • Deletes all data from all attached bindings. This is useful for resetting state between test blocks.

```ts import { reset } from "cloudflare:test"; import { afterEach } from "vitest";

afterEach(async () => { await reset(); }); ```

  • abortAllDurableObjects(): Promise\<void>
  • Resets all Durable Object instances. Unlike reset(), this does not delete persisted data. This forcibly tears down all running Durable Object instances, discarding in-memory state without waiting for in-flight requests to drain.

```ts import { abortAllDurableObjects } from "cloudflare:test"; import { afterEach } from "vitest";

afterEach(async () => { await abortAllDurableObjects(); }); ```

  • evictAllDurableObjects(options?:DurableObjectEvictionOptions): Promise\<void>
  • Evicts all currently-running Durable Objects in evictable namespaces. Unlike abortAllDurableObjects(), eviction is graceful: hibernatable WebSockets are hibernated rather than closed by default, and eviction waits up to 30 seconds for in-flight requests to drain. In-memory state is reset by tearing down each instance.

Non-running or idle Durable Objects are skipped, and namespaces with eviction prevented are respected. Accepts the same DurableObjectEvictionOptions as evictDurableObject().

```ts import { evictAllDurableObjects } from "cloudflare:test"; import { afterEach } from "vitest";

afterEach(async () => { await evictAllDurableObjects(); }); ```

D1

  • applyD1Migrations(db:D1Database, migrations:D1Migration[], migrationTableName?:string): Promise\<void>
  • Applies all un-applied D1 migrations stored in the migrations array to database db, recording migrations state in the migrationsTableName table. migrationsTableName defaults to d1_migrations. Call the readD1Migrations() function from the @cloudflare/vitest-plugin/config package inside Node.js to get the migrations array. Refer to the D1 recipe ↗︎ for an example project using migrations.

Secrets Store

Version

Available in @cloudflare/vitest-pool-workers version 0.13.5 and later, and in all versions of @cloudflare/vitest-plugin.

  • adminSecretsStore(binding:SecretsStoreSecret): SecretsStoreSecretAdmin
  • Returns an admin API for a Secrets Store binding. Secrets Store bindings only expose a read-only get() method, so use this helper to seed, update, or remove secret values from within a test. After you create a secret, binding.get() returns its value.

Declare the result with using so the admin API is released at the end of the scope. If the admin API is not disposed, workerd logs a warning.

On @cloudflare/vitest-pool-workers, declare the result with const instead, because using requires @cloudflare/vitest-plugin version 1.2.4 or later.

```ts import { adminSecretsStore } from "cloudflare:test"; import { env } from "cloudflare:workers"; import { expect, it } from "vitest";

it("reads a seeded secret", async () => { using admin = adminSecretsStore(env.MY_SECRET); await admin.create("test-value");

expect(await env.MY_SECRET.get()).toBe("test-value"); }); ```

The returned SecretsStoreSecretAdmin object has the following methods: - create(value:string): Promise\<string> - Creates a secret with the given value and returns its ID. - update(value:string, id:string): Promise\<string> - Updates the secret with the given ID to a new value and returns its ID. - duplicate(id:string, newName:string): Promise\<string> - Duplicates the secret with the given ID under a new name and returns the new secret's ID. - delete(id:string): Promise\<void> - Deletes the secret with the given ID. - list(): Promise<{ name: string; metadata?: { uuid: string } }[]> - Lists all secrets in the store. - get(id:string): Promise\<string> - Returns the name of the secret with the given ID. To read a secret's value, call get() on the binding instead.