Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 13 additions & 39 deletions src/content/docs/agents/getting-started/testing-your-agent.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,46 +22,21 @@ The `agents-starter` template and new Cloudflare Workers projects already includ
Before you write your first test, install the necessary packages:

```sh
npm install vitest@~3.0.0 --save-dev --save-exact
npm install @cloudflare/vitest-pool-workers --save-dev
npm install vitest@^4.1.0 @cloudflare/vitest-pool-workers --save-dev
```

Ensure that your `vitest.config.js` file is identical to the following:
Ensure that your `vitest.config.js` has the `cloudflareTest` plugin configured:

```js
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";

export default defineWorkersConfig({
test: {
poolOptions: {
workers: {
wrangler: { configPath: "./wrangler.jsonc" },
},
},
},
});
```

### Add the Agent configuration

Add a `durableObjects` configuration to `vitest.config.js` with the name of your Agent class:

```js
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";

export default defineWorkersConfig({
test: {
poolOptions: {
workers: {
main: "./src/index.ts",
miniflare: {
durableObjects: {
NAME: "MyAgent",
},
},
},
},
},
import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
import { defineConfig } from "vitest/config";

export default defineConfig({
plugins: [
cloudflareTest({
wrangler: { configPath: "./wrangler.jsonc" },
}),
],
});
```

Expand All @@ -76,11 +51,10 @@ Review the [Vitest documentation](https://vitest.dev/) for more information on t
Tests use the `vitest` framework. A basic test suite for your Agent can validate how your Agent responds to requests, but can also unit test your Agent's methods and state.

```ts
import { env, exports } from "cloudflare:workers";
import {
env,
createExecutionContext,
waitOnExecutionContext,
SELF,
} from "cloudflare:test";
import { describe, it, expect } from "vitest";
import worker from "../src";
Expand All @@ -104,7 +78,7 @@ describe("make a request to my Agent", () => {

it("also responds with state", async () => {
const request = new Request("http://example.com/agent/my-agent/agent-123");
const response = await SELF.fetch(request);
const response = await exports.default.fetch(request);
expect(await response.text()).toMatchObject({ hello: "from your agent" });
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1496,7 +1496,7 @@ This pattern does not scale. As traffic increases, the single Durable Object bec

### Test with Vitest and plan for class migrations

Use `@cloudflare/vitest-pool-workers` for testing Durable Objects. The integration provides isolated storage per test and utilities for direct instance access.
Use `@cloudflare/vitest-pool-workers` for testing Durable Objects. The integration provides utilities for direct instance access.

<TypeScriptExample filename="test/chat-room.test.ts">
```ts
Expand All @@ -1508,7 +1508,7 @@ import {
import { describe, it, expect } from "vitest";

describe("ChatRoom", () => {
// Each test gets isolated storage automatically

it("should send and retrieve messages", async () => {
const id = env.CHAT_ROOM.idFromName("test-room");
const stub = env.CHAT_ROOM.get(id);
Expand All @@ -1530,7 +1530,7 @@ const stub = env.CHAT_ROOM.get(id);
const count = state.storage.sql
.exec<{ count: number }>("SELECT COUNT(*) as count FROM messages")
.one();
expect(count.count).toBe(0); // Fresh instance due to test isolation
expect(count.count).toBe(2);
});

// Trigger alarms immediately without waiting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ import { env } from "cloudflare:workers";
import { describe, it, expect, beforeEach } from "vitest";

describe("Counter Durable Object", () => {
// Each test gets isolated storage automatically
it("should increment the counter", async () => {
const id = env.COUNTER.idFromName("test-counter");
const stub = env.COUNTER.get(id);
Expand All @@ -192,17 +191,11 @@ describe("Counter Durable Object", () => {
expect(count3).toBe(3);
});

it("should track separate counters independently", async () => {
it("should persist storage within a test file", async () => {
const id = env.COUNTER.idFromName("test-counter");
const stub = env.COUNTER.get(id);

await stub.increment("counter-a");
await stub.increment("counter-a");
await stub.increment("counter-b");

expect(await stub.getCount("counter-a")).toBe(2);
expect(await stub.getCount("counter-b")).toBe(1);
expect(await stub.getCount("counter-c")).toBe(0);
expect(await stub.getCount()).toBe(3);
});

it("should reset a counter", async () => {
Expand Down Expand Up @@ -342,40 +335,6 @@ describe("Direct Durable Object access", () => {
```
</TypeScriptExample>

### Test isolation

Each test automatically gets isolated storage. Durable Objects created in one test do not affect other tests:

<TypeScriptExample filename="test/isolation.test.ts">
```ts
import { env } from "cloudflare:workers";
import { listDurableObjectIds } from "cloudflare:test";
import { describe, it, expect } from "vitest";

describe("Test isolation", () => {
it("first test: creates a Durable Object", async () => {
const id = env.COUNTER.idFromName("isolated-counter");
const stub = env.COUNTER.get(id);

await stub.increment();
await stub.increment();
expect(await stub.getCount()).toBe(2);
});

it("second test: previous Durable Object does not exist", async () => {
// The Durable Object from the previous test is automatically cleaned up
const ids = await listDurableObjectIds(env.COUNTER);
expect(ids.length).toBe(0);

// Creating the same ID gives a fresh instance
const id = env.COUNTER.idFromName("isolated-counter");
const stub = env.COUNTER.get(id);
expect(await stub.getCount()).toBe(0);
});
});
```
</TypeScriptExample>

### Testing SQLite storage

SQLite-backed Durable Objects work seamlessly in tests. The SQL API is available when your Durable Object class is configured with `new_sqlite_classes` in your Wrangler configuration:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ Vitest's [fake timers](https://vitest.dev/guide/mocking.html#timers) do not appl

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.

### Durable Object alarms

Durable Object alarms are not reset between test runs and do not respect isolated storage. Ensure you delete or run all alarms with [`runDurableObjectAlarm()`](/workers/testing/vitest-integration/test-apis/#durable-objects) scheduled in each test before finishing the test.

### WebSockets

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`.
Expand Down
19 changes: 10 additions & 9 deletions src/content/docs/workers/testing/vitest-integration/recipes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,22 @@ description: Examples that demonstrate how to write unit and integration

Recipes are examples that help demonstrate how to write unit tests and integration tests for Workers projects using the [`@cloudflare/vitest-pool-workers`](https://www.npmjs.com/package/@cloudflare/vitest-pool-workers) package.

- [Basic unit and integration tests for Workers using `SELF`](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/basics-unit-integration-self)
- [Basic unit and integration tests for Pages Functions using `SELF`](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/pages-functions-unit-integration-self)
- [Basic unit and integration tests for Workers using `exports.default`](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/basics-unit-integration-self)
- [Basic unit and integration tests for Pages Functions using `exports.default`](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/pages-functions-unit-integration-self)
- [Basic integration tests using an auxiliary Worker](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/basics-integration-auxiliary)
- [Basic integration test for Workers with static assets](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/workers-assets)
- [Isolated tests using KV, R2 and the Cache API](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/kv-r2-caches)
- [Isolated tests using D1 with migrations](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/d1)
- [Isolated tests using Durable Objects with direct access](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/durable-objects)
- [Isolated tests using Workflows](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/workflows)
- [Tests using KV, R2 and the Cache API](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/kv-r2-caches)
- [Tests using D1 with migrations](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/d1)
- [Tests using Durable Objects with direct access](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/durable-objects)
- [Tests using Workflows](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/workflows)
- [Tests using Queue producers and consumers](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/queues)
- [Tests using Pipelines](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/pipelines)
- [Tests using Hyperdrive with a Vitest managed TCP server](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/hyperdrive)
- [Tests using declarative/imperative outbound request mocks](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/request-mocking)
- [Tests using declarative (MSW) / imperative outbound request mocks](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/request-mocking)
- [Tests using multiple auxiliary Workers and request mocks](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/multiple-workers)
- [Tests importing WebAssembly modules](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/web-assembly)
- [Tests using JSRPC with entrypoints and Durable Objects](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/rpc)
- [Tests using `ctx.exports` to access Worker exports](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/context-exports)
- [Integration test with static assets and Puppeteer](https://github.com/GregBrimble/puppeteer-vitest-workers-assets)
- [Resolving modules with Vite Dependency Pre-Bundling](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/module-resolution)
- [Mocking Workers AI and Vectorize bindings in unit tests](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/ai-vectorize)
- [Tests using the Images binding](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/images)
- [Tests mocking Workers Assets](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/workers-assets)
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,6 @@ You should also add the output of `wrangler types` to the `include` array so tha
```
</Details>

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`.

```ts title="test/env.d.ts"
declare module "cloudflare:workers" {
// ProvidedEnv controls the type of `import("cloudflare:workers").env`
interface ProvidedEnv extends Env {}
}
```

If your test bindings differ from the bindings in your Wrangler config, you should type them here in `ProvidedEnv`.

## Writing tests

Expand Down
Loading