diff --git a/assets/images/workers/testing/vitest/isolation-model-1-no-isolated-storage-no-single-worker.svg b/assets/images/workers/testing/vitest/isolation-model-1-no-isolated-storage-no-single-worker.svg
new file mode 100644
index 00000000000000..a862b45bafd569
--- /dev/null
+++ b/assets/images/workers/testing/vitest/isolation-model-1-no-isolated-storage-no-single-worker.svg
@@ -0,0 +1,27 @@
+
diff --git a/assets/images/workers/testing/vitest/isolation-model-2-no-isolated-storage-single-worker.svg b/assets/images/workers/testing/vitest/isolation-model-2-no-isolated-storage-single-worker.svg
new file mode 100644
index 00000000000000..cbcb0bd93b6816
--- /dev/null
+++ b/assets/images/workers/testing/vitest/isolation-model-2-no-isolated-storage-single-worker.svg
@@ -0,0 +1,25 @@
+
diff --git a/assets/images/workers/testing/vitest/isolation-model-3-isolated-storage-no-single-worker.svg b/assets/images/workers/testing/vitest/isolation-model-3-isolated-storage-no-single-worker.svg
new file mode 100644
index 00000000000000..d8ed8bf9062efa
--- /dev/null
+++ b/assets/images/workers/testing/vitest/isolation-model-3-isolated-storage-no-single-worker.svg
@@ -0,0 +1,39 @@
+
diff --git a/assets/images/workers/testing/vitest/isolation-model-4-isolated-storage-single-worker.svg b/assets/images/workers/testing/vitest/isolation-model-4-isolated-storage-single-worker.svg
new file mode 100644
index 00000000000000..9cdf7d5af684c7
--- /dev/null
+++ b/assets/images/workers/testing/vitest/isolation-model-4-isolated-storage-single-worker.svg
@@ -0,0 +1,25 @@
+
diff --git a/components/CodeCopy.vue b/components/CodeCopy.vue
index 9834f0503dcdb0..fe4821e7e6a326 100644
--- a/components/CodeCopy.vue
+++ b/components/CodeCopy.vue
@@ -88,6 +88,7 @@ function copyCode(e: MouseEvent) {
width: 2rem;
font-size: 0.9rem;
padding: 0.5rem;
+ box-sizing: border-box;
background: #d9d9d9;
color: #1e1e1e;
border: none;
diff --git a/content/_redirects b/content/_redirects
index 3cb64c3ad59556..d7823d8ba49d8a 100644
--- a/content/_redirects
+++ b/content/_redirects
@@ -1274,6 +1274,7 @@
/workers/runtime-apis/mtls/ /workers/runtime-apis/bindings/mtls/ 301
/workers/runtime-apis/queues/ /workers/runtime-apis/bindings/bindings/queues/ 301
/workers/runtime-apis/service-bindings/ /workers/runtime-apis/bindings/service-bindings/ 301
+/workers/observability/local-development-and-testing/ /workers/testing/local-development/ 301
# workers ai
/workers-ai/models/llm/ /workers-ai/models/#text-generation 301
diff --git a/content/d1/observability/debug-d1.md b/content/d1/observability/debug-d1.md
index df7aa5c88ee391..006d420bca062f 100644
--- a/content/d1/observability/debug-d1.md
+++ b/content/d1/observability/debug-d1.md
@@ -10,7 +10,7 @@ D1 allows you to capture exceptions and log errors returned when querying a data
## Handle errors
-The D1 [client API](/d1/build-databases/query-databases/) returns detailed [error messages](/d1/build-databases/query-databases/#errors) within an `Error` object.
+The D1 [client API](/d1/build-databases/query-databases/) returns detailed [error messages](/d1/build-databases/query-databases/#errors) within an `Error` object.
To ensure you are capturing the full error message, log or return `e.message` as follows:
@@ -56,4 +56,4 @@ You should include as much of the following in any bug report:
* Learn [how to debug Workers](/workers/observability/).
* Understand how to [access logs](/workers/observability/logging/) generated from your Worker and D1.
-* Use [`wrangler dev`](/workers/wrangler/commands/#dev) to run your Worker and D1 locally and [debug issues before deploying](/workers/observability/local-development-and-testing/).
+* Use [`wrangler dev`](/workers/wrangler/commands/#dev) to run your Worker and D1 locally and [debug issues before deploying](/workers/testing/local-development/).
diff --git a/content/learning-paths/workers/test/intro-to-observability.md b/content/learning-paths/workers/test/intro-to-observability.md
index 7def01320fb963..3413fe38798c7a 100644
--- a/content/learning-paths/workers/test/intro-to-observability.md
+++ b/content/learning-paths/workers/test/intro-to-observability.md
@@ -13,7 +13,7 @@ To explore observability, debugging and testing options for Workers, refer to [O
### Local development
-Refer to [Local development and testing](/workers/observability/local-development-and-testing/) to learn how to test your Worker locally with `wrangler dev`.
+Refer to [Local development and testing](/workers/testing/local-development/) to learn how to test your Worker locally with `wrangler dev`.
### Logs
diff --git a/content/workers/_partials/_testing-pages-functions.md b/content/workers/_partials/_testing-pages-functions.md
new file mode 100644
index 00000000000000..92c1124c6f2155
--- /dev/null
+++ b/content/workers/_partials/_testing-pages-functions.md
@@ -0,0 +1,12 @@
+---
+_build:
+ publishResources: false
+ render: never
+ list: never
+---
+
+{{}}
\ No newline at end of file
diff --git a/content/workers/observability/_index.md b/content/workers/observability/_index.md
index 9135864264d5bf..260970b3dc35fb 100644
--- a/content/workers/observability/_index.md
+++ b/content/workers/observability/_index.md
@@ -1,11 +1,11 @@
---
pcx_content_type: navigation
title: Observability
-weight: 9
+weight: 10
---
# Observability
-Test and debug your Worker projects.
+Understand how your Worker projects are performing via logs, traces, and other data sources.
{{}}
\ No newline at end of file
diff --git a/content/workers/observability/local-development-and-testing.md b/content/workers/observability/local-development-and-testing.md
deleted file mode 100644
index d673439df5f58e..00000000000000
--- a/content/workers/observability/local-development-and-testing.md
+++ /dev/null
@@ -1,136 +0,0 @@
----
-title: Local development and testing
-pcx_content_type: concept
-meta:
- description: Test your Worker in local development.
----
-
-# Local development and testing
-
-Cloudflare Workers can be fully developed and tested locally - providing confidence that the applications you develop locally work the same way in production. This allows you to be more efficient and effective by providing a faster feedback loop and removing the need to test against remote resources. Local development runs against the same production runtime used by Cloudflare Workers, [workerd](https://github.com/cloudflare/workerd).
-
-In addition to testing Workers locally with `wrangler dev`, the use of Miniflare allows you to test other Developer Platform products locally, such as [R2](/r2/), [KV](/kv/), [D1](/d1/), and [Durable Objects](/durable-objects/).
-
-
-## Start a local development server
-
-{{}}
-
-Wrangler provides a [`dev`](/workers/wrangler/commands/#dev) command that starts a local server for developing your Worker. Make sure you have `npm` installed and run the following in the folder containing your Worker application:
-
-```sh
-$ npx wrangler dev
-```
-
-`wrangler dev` will run the preview of the Worker directly on your local machine. `wrangler dev` uses a combination of `workerd` and [Miniflare](https://github.com/cloudflare/workers-sdk/tree/main/packages/miniflare), a simulator that allows you to test your Worker against additional resources like KV, Durable Objects, WebSockets, and more. Resources such as KV, Durable Objects, D1, and R2 will be stored and persisted locally and not affect live production or preview data.
-
-### Clear Wrangler's local storage
-
-Wrangler will store all locally created resources and storage in a `.wrangler` folder inside your Worker directory. This folder should be added to your `.gitignore` file.
-
-If you need to clear local storage, delete the `.wrangler/state` folder. It will be recreated the next time you run `wrangler dev`.
-
-### Develop locally using remote resources and bindings
-
-{{}}
-
-`wrangler dev` runs locally by default. This means that all resources and bindings are simulated locally as well. However, there may be times you need to develop against remote resources and bindings. To run `wrangler dev` remotely, add the `--remote` flag:
-
-```sh
-$ npx wrangler dev --remote
-```
-
-Remote resources to use during `wrangler dev --remote` are specified with preview ID/names such as `preview_id` or `preview_bucket name`. Preview resources can be resources separate from production resources to prevent changing production data in development. `wrangler dev --remote` only supports preview ID/names for storage resources such as KV, R2, and D1. To change production data in `wrangler dev --remote`, set the preview ID/name of the resource to the ID/name of the production resource.
-
-### Customize `wrangler dev`
-
-You can customize how `wrangler dev` works to fit your needs. Refer to [the `wrangler dev` documentation](/workers/wrangler/commands/#dev) for available configuration options.
-
-{{}}
-
-## DevTools
-
-Wrangler supports using the [Chrome DevTools](https://developer.chrome.com/docs/devtools/) to view logs/sources, set breakpoints, and profile CPU/memory usage. With `wrangler dev` running, press the d key in your terminal to open a DevTools session connected to your Worker from any Chromium-based browser.
-
-## Debug via breakpoints
-
-As of Wrangler 3.9.0, you can debug via breakpoints in your Worker. Breakpoints provide the ability to see exactly what is happening at a given point in the execution of your Worker. This functionality exists in both DevTools and VS Code.
-
-For more information on breakpoint debugging via Chrome's DevTools, refer to [Chrome's article on breakpoints](https://developer.chrome.com/docs/devtools/javascript/breakpoints/).
-
-### Setup VS Code to use breakpoints
-
-To setup VS Code for breakpoint debugging in your Worker project:
-
-1. Create a `.vscode` folder in your project's root folder if one does not exist.
-2. Within that folder, create a `launch.json` file with the following content:
-
-```json
-{
- "configurations": [
- {
- "name": "Wrangler",
- "type": "node",
- "request": "attach",
- "port": 9229,
- "cwd": "/",
- "resolveSourceMapLocations": null,
- "attachExistingChildren": false,
- "autoAttachChildProcesses": false,
- "sourceMaps": true // works with or without this line
- }
- ]
-}
-```
-
-3. Open your project in VS Code, open a new terminal window from VS Code, and run `npx wrangler dev` to start the local dev server.
-
-4. At the top of the **Run & Debug** panel, you should see an option to select a configuration. Choose **Wrangler**, and select the play icon. You should see **Wrangler: Remote Process [0]** show up in the Call Stack panel on the left.
-
-5. Go back to a `.js` or `.ts` file in your project and add at least one breakpoint.
-
-5. Open your browser and go to the Worker's local URL (default `http://127.0.0.1:8787`). The breakpoint should be hit, and you should see details about your code at the specified line.
-
-{{}}
-
-{{}}
-
-## Test Workers
-
-### Integration testing
-
-Wrangler offers an experimental API, `unstable_dev`, that will allow you to start a server for integration testing.
-
-For more information and examples, refer to the [`unstable_dev` guide](/workers/wrangler/api/#unstable_dev).
-
-### Advanced local testing via Miniflare
-
-[Miniflare](https://github.com/cloudflare/workers-sdk/tree/main/packages/miniflare#readme) is a simulator for developing and testing Workers. It supports simulating and mocking resources like: KV, Durable Objects, R2, D1, and Queues.
-
-Miniflare is fully local, and is built on top of the Workers runtime, [`workerd`](https://github.com/cloudflare/workerd) to ensure that local behavior accurately reflects production. All of this makes it a great tool for writing tests or advanced use cases.
-
-## Related resources
-
-* [Log from Workers](/workers/observability/logging/real-time-logs) - Access logs and exceptions for your Workers using the dashboard or [`wrangler tail`](/workers/wrangler/commands/#tail).
diff --git a/content/workers/observability/logging/real-time-logs.md b/content/workers/observability/logging/real-time-logs.md
index 7772240d731bf9..86bddf6d8838f4 100644
--- a/content/workers/observability/logging/real-time-logs.md
+++ b/content/workers/observability/logging/real-time-logs.md
@@ -74,7 +74,7 @@ To view real-time logs associated with any deployed Worker using the Cloudflare
1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com) and select your account.
2. In Account Home, go to **Workers & Pages**.
-3. In **Overview**, select your **Worker** > and select **Logs**.
+3. In **Overview**, select your **Worker** > and select **Logs**.
## View logs using `wrangler tail`
@@ -151,6 +151,6 @@ Refer to the [Tail Workers documentation](/workers/observability/logging/tail-wo
## Related resources
* [Errors and exceptions](/workers/observability/errors/) - Review common Workers errors.
-* [Local development and testing](/workers/observability/local-development-and-testing/) - Develop and test you Workers locally.
+* [Local development and testing](/workers/testing/local-development/) - Develop and test you Workers locally.
* [Logpush](/workers/observability/logging/logpush/) - Learn how to push Workers Trace Event Logs to supported destinations.
* [Tail Workers](/workers/observability/logging/logpush/) - Learn how to attach Tail Workers to transform your logs and send them to HTTP endpoints.
diff --git a/content/workers/testing/_index.md b/content/workers/testing/_index.md
new file mode 100644
index 00000000000000..ab96a73621fc18
--- /dev/null
+++ b/content/workers/testing/_index.md
@@ -0,0 +1,31 @@
+---
+pcx_content_type: navigation
+title: Testing
+weight: 9
+---
+
+# Testing
+
+Review the tools available for testing and debugging Workers.
+
+{{}}
+
+## Testing comparison matrix
+
+| Feature | Vitest Pool | `unstable_dev()` | Miniflare's API |
+| ----------------------------------------- | ---------------- | ---------------- | -------------------- |
+| Unit testing | ✅ | ❌ | ❌ |
+| Integration testing | ✅ | ✅ | ✅ |
+| Loading Wrangler configuration files | ✅ | ✅ | ❌ |
+| Bindings directly in tests | ✅ | ❌ | ✅ |
+| Isolated per-test storage | ✅ | ❌ | ❌ |
+| Outbound request mocking | ✅ | ❌ | ✅ |
+| Multiple Worker support | ✅ | 🚧[^1] | ✅ |
+| Direct access to Durable Object instances | ✅ | ❌ | ❌ |
+| Run Durable Object alarms immediately | ✅ | ❌ | ❌ |
+| List Durable Objects | ✅ | ❌ | ❌ |
+| Testing service Workers | ❌ | ✅ | ✅ |
+
+[^1]: Support for multiple Workers in [`unstable_dev()`](/workers/wrangler/api/#unstable_dev) relies on `wrangler dev`'s service registry which can be unreliable when running multiple tests in parallel.
+
+{{}}
diff --git a/content/workers/testing/debugging-tools.md b/content/workers/testing/debugging-tools.md
new file mode 100644
index 00000000000000..f4272c43df5ebf
--- /dev/null
+++ b/content/workers/testing/debugging-tools.md
@@ -0,0 +1,72 @@
+---
+title: Debugging tools
+weight: 4
+pcx_content_type: concept
+meta:
+ description: Debug your local and deployed Workers using a variety of tools.
+---
+
+# Debugging tools
+
+Being able to assess how your Workers are functioning at various points in the development cycle is vital to identifying the root causes of bugs or issues.
+
+Cloudflare provides a variety of tools to help you debug your Workers.
+
+## Chrome DevTools
+
+Wrangler supports using [Chrome DevTools](https://developer.chrome.com/docs/devtools/) to view logs/sources, set breakpoints, and profile CPU/memory usage. To open a DevTools session connected to your Worker from any Chromium-based browser, run [`wrangler dev`](/workers/wrangler/commands/#dev) and press the d key in your terminal.
+
+## Debug via breakpoints
+
+As of Wrangler 3.9.0, you can debug via breakpoints in your Worker. Breakpoints provide the ability to review what is happening at a given point in the execution of your Worker. Breakpoint functionality exists in both DevTools and VS Code.
+
+For more information on breakpoint debugging via Chrome's DevTools, refer to [Chrome's article on breakpoints](https://developer.chrome.com/docs/devtools/javascript/breakpoints/).
+
+### Setup VS Code to use breakpoints
+
+To setup VS Code for breakpoint debugging in your Worker project:
+
+1. Create a `.vscode` folder in your project's root folder if one does not exist.
+2. Within that folder, create a `launch.json` file with the following content:
+
+```json
+{
+ "configurations": [
+ {
+ "name": "Wrangler",
+ "type": "node",
+ "request": "attach",
+ "port": 9229,
+ "cwd": "/",
+ "resolveSourceMapLocations": null,
+ "attachExistingChildren": false,
+ "autoAttachChildProcesses": false,
+ "sourceMaps": true // works with or without this line
+ }
+ ]
+}
+```
+
+3. Open your project in VS Code, open a new terminal window from VS Code, and run `npx wrangler dev` to start the local dev server.
+
+4. At the top of the **Run & Debug** panel, you should see an option to select a configuration. Choose **Wrangler**, and select the play icon. **Wrangler: Remote Process [0]** should show up in the Call Stack panel on the left.
+
+5. Go back to a `.js` or `.ts` file in your project and add at least one breakpoint.
+
+5. Open your browser and go to the Worker's local URL (default `http://127.0.0.1:8787`). The breakpoint should be hit, and you should be able to review details about your code at the specified line.
+
+{{}}
+
+{{}}
+
+## Related resources
+
+* [Local Development](/workers/testing/local-development/) - Develop your Workers and connected resources locally via Wrangler and [`workerd`](https://github.com/cloudflare/workerd), for a fast, accurate feedback loop.
diff --git a/content/workers/testing/integration-testing.md b/content/workers/testing/integration-testing.md
new file mode 100644
index 00000000000000..819bb73934202b
--- /dev/null
+++ b/content/workers/testing/integration-testing.md
@@ -0,0 +1,146 @@
+---
+title: Integration testing
+weight: 3
+pcx_content_type: concept
+meta:
+ description: Test multiple units of your Worker working together.
+---
+
+# Integration testing
+
+Integration tests test multiple units of your Worker together by sending HTTP requests to your Worker and asserting on the HTTP responses. As an example, consider the following Worker:
+
+```js
+---
+filename: index.mjs
+---
+export function add(a, b) {
+ return a + b;
+}
+
+export default {
+ async fetch(request) {
+ const url = new URL(request.url);
+ const a = parseInt(url.searchParams.get("a"));
+ const b = parseInt(url.searchParams.get("b"));
+ return new Response(add(a, b));
+ }
+}
+```
+
+An integration test for this Worker might look like the following example:
+
+```js
+// Start Worker HTTP server on port 8787 running `index.mjs` then...
+const response = await fetch("http://localhost:8787/?a=1&b=2");
+
+assert((await response.text()) === "3");
+```
+
+In the above example, instead of importing the `add` function as a [unit test](/workers/testing/unit-testing/) would do, you make a direct call to the endpoint, testing that the Worker responds at the endpoint with the appropriate response.
+
+## Vitest integration
+
+The recommended way to write integration tests for your Workers is by using [the Workers Vitest integration](/workers/testing/vitest-integration/get-started/). Vitest can be configured to run integrations against a single Worker or multiple Workers.
+
+### Testing via `SELF`
+
+If testing a single Worker, you can use the `SELF` fetcher provided by the [`@cloudflare/test` API](/workers/testing/vitest-integration/test-apis/).
+
+```js
+---
+filename: index.spec.js
+---
+import { SELF } from "cloudflare:test";
+
+it("dispatches fetch event", async () => {
+ const response = await SELF.fetch("https://example.com");
+ expect(await response.text()).toMatchInlineSnapshot(...);
+});
+```
+
+When using `SELF` 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 same subtly different module resolution behavior provided by Vite.
+
+Usually this is not a problem, but if you would like to run your Worker in a fresh environment that is as close to production as possible, using an auxiliary Worker may be a good idea. Auxiliary Workers have some developer experience (DX) limitations.
+
+### Testing via auxiliary Workers
+
+It is also possible to configure Workers for integration testing via `vitest.config.ts`. An [example `vitest.config.ts` configuration file](https://github.com/cloudflare/workers-sdk/blob/main/fixtures/vitest-pool-workers-examples/basics-integration-auxiliary/vitest.config.ts) on GitHub.
+
+The Worker can then be referenced like the following example:
+
+```js
+import { env } from "cloudflare:test";
+import { expect, it } from "vitest";
+
+it("dispatches fetch event", async () => {
+ const response = await env.WORKER.fetch("http://example.com");
+ expect(await response.text()).toBe("👋");
+});
+```
+
+Instead of running the Worker-under-test in the same Worker as the test runner like `SELF`, this example defines the Worker-under-test as an _auxiliary_ Worker. This means the Worker runs in a separate isolate to the test runner, with a different global scope. The Worker-under-test runs in an environment closer to production, but Vite transformations and hot-module-reloading aren't applied to the Worker—you must compile your TypeScript to JavaScript beforehand.
+
+Auxiliary Workers cannot be configured from `wrangler.toml` files. You must use Miniflare [`WorkerOptions`](https://github.com/cloudflare/workers-sdk/tree/main/packages/miniflare#interface-workeroptions) in `vitest.config.ts`.
+
+{{}}
+
+
+## Wrangler's `unstable_dev()` API
+
+If you do not want to use Vitest and would like to write integration tests for a single Worker, consider using [Wrangler's `unstable_dev()` API](/workers/wrangler/api/#unstable_dev). `unstable_dev()` allows you to start an HTTP server similar to [`wrangler dev`](/workers/wrangler/commands/#dev) that you can send HTTP requests to. `unstable_dev()` will automatically load options from your Wrangler configuration file. Note that `unstable_dev()` is an experimental API subject to breaking changes.
+
+```js
+import assert from "node:assert";
+import { unstable_dev } from "wrangler";
+
+const worker = await unstable_dev("./index.mjs");
+try {
+ const response = await worker.fetch("/?a=1&b=2");
+ assert.strictEqual(await response.text(), "3");
+} finally {
+ await worker.stop();
+}
+```
+
+{{}}
+
+## Miniflare's API
+
+If you would like to write integration tests for multiple Workers, need direct access to [bindings](/workers/configuration/bindings/) outside your Worker in tests, or have another advanced use case, consider using [Miniflare's API](https://github.com/cloudflare/workers-sdk/blob/main/packages/miniflare/README.md) directly. Miniflare is the foundation for the other testing tools on this page, exposing a JavaScript API for the [`workerd` runtime](https://github.com/cloudflare/workerd) and local simulators for the other Developer Platform products. Unlike `unstable_dev()`, Miniflare does not automatically load options from your Wrangler configuration file.
+
+```js
+import assert from "node:assert";
+import { Miniflare } from "miniflare";
+
+const mf = new Miniflare({
+ modules: true,
+ scriptPath: "./index.mjs",
+});
+try {
+ const response = await mf.dispatchFetch("http://example.com/?a=1&b=2");
+ assert.strictEqual(await response.text(), "3");
+} finally {
+ await mf.dispose();
+}
+```
+
+{{}}
+
+{{}}
+
+## Related Resources
+
+- [Recipes](/workers/testing/vitest-integration/recipes/) - Example integration tests for Workers using the Workers Vitest integration.
diff --git a/content/workers/testing/local-development.md b/content/workers/testing/local-development.md
new file mode 100644
index 00000000000000..df0e73448730e6
--- /dev/null
+++ b/content/workers/testing/local-development.md
@@ -0,0 +1,84 @@
+---
+title: Local development
+weight: 2
+pcx_content_type: concept
+meta:
+ description: Develop your Workers locally via Wrangler.
+---
+
+# Local development
+
+Cloudflare Workers and most connected resources can be fully developed and tested locally - providing confidence that the applications you build locally will work the same way in production. This allows you to be more efficient and effective by providing a faster feedback loop and removing the need to test against remote resources. Local development runs against the same production runtime used by Cloudflare Workers, [workerd](https://github.com/cloudflare/workerd).
+
+In addition to testing Workers locally with [`wrangler dev`](/workers/wrangler/commands/#dev), the use of Miniflare allows you to test other Developer Platform products locally, such as [R2](/r2/), [KV](/kv/), [D1](/d1/), and [Durable Objects](/durable-objects/).
+
+## Start a local development server
+
+{{}}
+
+Wrangler provides a [`dev`](/workers/wrangler/commands/#dev) command that starts a local server for developing your Worker. Make sure you have `npm` installed and run the following in the folder containing your Worker application:
+
+```sh
+$ npx wrangler dev
+```
+
+`wrangler dev` will run the preview of the Worker directly on your local machine. `wrangler dev` uses a combination of `workerd` and [Miniflare](https://github.com/cloudflare/workers-sdk/tree/main/packages/miniflare), a simulator that allows you to test your Worker against additional resources like KV, Durable Objects, WebSockets, and more.
+
+Resources such as KV, Durable Objects, D1, and R2 will be stored and persisted locally and not affect live production or preview data. Wrangler will automatically create local versions of bindings found in `wrangler.toml`. These will not have data in them initially, so you will need to add data manually.
+
+### Supported resource bindings in local development
+
+| Product | Supported |
+| ----------------------------------------- | --------- |
+| R2 | ✅ |
+| KV | ✅ |
+| D1 | ✅ |
+| Durable Objects | ✅ |
+| Queues | ✅ |
+| Service Bindings (multiple workers) | ✅ |
+| AI | ✅[^1] |
+| Hyperdrive | ✅ |
+
+[^1]: Using Workers AI always accesses your Cloudflare account in order to run AI models and will incur usage charges even in local development.
+
+### Clear Wrangler's local storage
+
+Wrangler will store all locally created resources and storage in a `.wrangler` folder inside your Worker directory. This folder should be added to your `.gitignore` file.
+
+If you need to clear local storage, delete the `.wrangler/state` folder. It will be recreated the next time you run `wrangler dev`.
+
+### Develop locally using remote resources and bindings
+
+{{}}
+
+`wrangler dev` runs locally by default. This means that all resources and bindings are simulated locally as well. However, there may be times you need to develop against remote resources and bindings. To run `wrangler dev` remotely, add the `--remote` flag:
+
+```sh
+$ npx wrangler dev --remote
+```
+
+Remote resources to use during `wrangler dev --remote` are specified with preview ID/names such as `preview_id` or `preview_bucket name`. Preview resources can be resources separate from production resources to prevent changing production data in development. `wrangler dev --remote` only supports preview ID/names for storage resources such as KV, R2, and D1. To change production data in `wrangler dev --remote`, set the preview ID/name of the resource to the ID/name of the production resource.
+
+### Customize `wrangler dev`
+
+You can customize how `wrangler dev` works to fit your needs. Refer to [the `wrangler dev` documentation](/workers/wrangler/commands/#dev) for available configuration options.
+
+{{}}
+
+## Related resources
+
+* [Debugging tools](/workers/testing/debugging-tools) - Tools to help you diagnose issues and gain insight into your Workers.
diff --git a/content/workers/testing/unit-testing.md b/content/workers/testing/unit-testing.md
new file mode 100644
index 00000000000000..62b9cc159bf31b
--- /dev/null
+++ b/content/workers/testing/unit-testing.md
@@ -0,0 +1,49 @@
+---
+title: Unit testing
+weight: 2
+pcx_content_type: concept
+meta:
+ description: Test independent units of your Worker by importing them into your tests.
+---
+
+# Unit testing
+
+In a Workers context, a unit test imports and directly calls functions from your Worker. After calling the functions, the unit test then asserts on the functions' return values. For example, consider you have the following Worker:
+
+```js
+---
+filename: index.mjs
+---
+export function add(a, b) {
+ return a + b;
+}
+
+export default {
+ async fetch(request) {
+ const url = new URL(request.url);
+ const a = parseInt(url.searchParams.get("a"));
+ const b = parseInt(url.searchParams.get("b"));
+ return new Response(add(a, b));
+ }
+}
+```
+
+An example unit test for the above Worker may look like the following:
+
+```js
+import { add } from "./index.mjs";
+
+assert(add(1, 2) === 3);
+```
+
+This test only assets that the `add` function is returning the correct value, but does not test the Worker itself like an [integration test](/workers/testing/integration-testing) would.
+
+## Vitest integration
+
+The recommended way to unit test your Workers is by using the Workers Vitest integration. For more information on features, as well as installation and setup instructions, refer to the [Vitest integration Get Started guide](/workers/testing/vitest-integration/get-started/)
+
+{{}}
+
+## Related Resources
+
+- [Recipes](/workers/testing/vitest-integration/recipes/) - Examples of unit tests using the Workers Vitest integration.
diff --git a/content/workers/testing/vitest-integration/_index.md b/content/workers/testing/vitest-integration/_index.md
new file mode 100644
index 00000000000000..238da8587dfa3d
--- /dev/null
+++ b/content/workers/testing/vitest-integration/_index.md
@@ -0,0 +1,13 @@
+---
+pcx_content_type: navigation
+title: Vitest integration
+weight: 5
+---
+
+# Vitest integration
+
+Information about the Workers Vitest integration - the recommended package for writing unit and integration tests for Workers.
+
+{{}}
+
+{{}}
diff --git a/content/workers/testing/vitest-integration/configuration.md b/content/workers/testing/vitest-integration/configuration.md
new file mode 100644
index 00000000000000..6e1b29229fc050
--- /dev/null
+++ b/content/workers/testing/vitest-integration/configuration.md
@@ -0,0 +1,224 @@
+---
+title: Configuration
+pcx_content_type: reference
+weight: 4
+---
+
+# Configuration
+
+The Workers Vitest integration provides additional configuration on top of Vitest's usual options. To configure the Workers Vitest integration, use the `poolOptions.workers` key. Use the [`defineWorkersConfig()`](/workers/testing/vitest-integration/configuration/#functions) function from the `@cloudflare/vitest-pool-workers/config` module for type checking and completions.
+
+An example configuration would be:
+
+```ts
+---
+filename: vitest.config.ts
+---
+import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
+
+export default defineWorkersConfig({
+ test: {
+ poolOptions: {
+ workers: {
+ wrangler: {
+ configPath: "./wrangler.toml"
+ },
+ },
+ },
+ },
+});
+```
+
+{{}}
+
+{{}}
+
+## Functions
+
+The following functions are exported from the `@cloudflare/vitest-pool-workers/config` module.
+
+{{}}
+
+- {{}}defineWorkersConfig(options:{{}}UserConfig & { test?: { poolOptions?: { workers?: WorkersPoolOptions | ((ctx: WorkerPoolOptionsContext) => Awaitable\) } } }{{}}){{}}
+
+ - Ensures Vitest is configured to use the Workers integration with the correct module resolution settings, and provides type checking for `WorkersPoolOptions`. This should be used in place of the [`defineConfig()`](https://vitest.dev/config/file.html) function from Vitest. The `defineWorkersConfig()` function also accepts a `Promise` of `options`, or an optionally-`async` function returning `options`.
+
+- {{}}defineWorkersProject(options:{{}}UserWorkspaceConfig & { test?: { poolOptions?: { workers?: WorkersPoolOptions | ((ctx: WorkerPoolOptionsContext) => Awaitable\) } } }{{}}){{}}
+
+ - Ensures Vitest is configured to use the Workers integration with the correct module resolution settings, and provides type checking for `WorkersPoolOptions`. This should be used in place of the [`defineProject()`](https://vitest.dev/guide/workspace) function from Vitest. The `defineWorkersProject()` function also accepts a `Promise` of `options`, or an optionally-`async` function returning `options`.
+
+- {{}}readD1Migrations(migrationsPath:{{}}string{{}}){{}}: {{}}D1Migration[]{{}}
+
+ - Reads all [D1 migrations](/d1/reference/migrations/) stored at `migrationsPath` and returns them ordered by migration number. Each migration will have its contents split into an array of individual SQL queries. Call the [`applyD1Migrations()`](/workers/testing/vitest-integration/test-apis/#d1) function inside a test or [setup file](https://vitest.dev/config/#setupfiles) to apply migrations. Refer to the [D1 recipe](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/d1) for an example project using migrations.
+
+{{}}
+
+## `WorkersPoolOptions`
+
+{{}}
+
+- {{}}main{{}}: {{}}string{{}}{{}}optional{{}}
+
+ - Entry point to Worker run in the same isolate/context as tests. This option is required to use `import { SELF } from "cloudflare:test"` for integration tests, or Durable Objects without an explicit `scriptName` if classes are defined in the same Worker. This file goes through Vite transforms and can be TypeScript. Note that `import module from ""` inside tests gives exactly the same `module` instance as is used internally for the `SELF` and Durable Object bindings. If `wrangler.configPath` is defined and this option is not, it will be read from the `main` field in that configuration file.
+
+- {{}}isolatedStorage{{}}: {{}}boolean{{}}{{}}optional{{}}
+
+ - Enables per-test isolated storage. If enabled, any writes to storage performed in a test will be undone at the end of the test. The test's storage environment is copied from the containing suite, meaning `beforeAll()` hooks can be used to seed data. If this option is disabled, all tests will share the same storage. `.concurrent` tests are not supported when isolated storage is enabled. Refer to the [Isolation and concurrency](/workers/testing/vitest-integration/isolation-and-concurrency/) page for more information on the isolation model.
+
+ - Defaults to `true`.
+
+
+ Illustrative example
+
+ ```ts
+ import { env } from "cloudflare:test";
+ import { beforeAll, beforeEach, describe, test, expect } from "vitest";
+
+ // Get the current list stored in a KV namespace
+ async function get(): Promise {
+ return await env.NAMESPACE.get("list", "json") ?? [];
+ }
+ // Add an item to the end of the list
+ async function append(item: string) {
+ const value = await get();
+ value.push(item);
+ await env.NAMESPACE.put("list", JSON.stringify(value));
+ }
+
+ beforeAll(() => append("all"));
+ beforeEach(() => append("each"));
+
+ test("one", async () => {
+ // Each test gets its own storage environment copied from the parent
+ await append("one");
+ expect(await get()).toStrictEqual(["all", "each", "one"]);
+ });
+ // `append("each")` and `append("one")` undone
+ test("two", async () => {
+ await append("two");
+ expect(await get()).toStrictEqual(["all", "each", "two"]);
+ });
+ // `append("each")` and `append("two")` undone
+
+ describe("describe", async () => {
+ beforeAll(() => append("describe all"));
+ beforeEach(() => append("describe each"));
+
+ test("three", async () => {
+ await append("three");
+ expect(await get()).toStrictEqual([
+ // All `beforeAll()`s run before `beforeEach()`s
+ "all", "describe all", "each", "describe each", "three"
+ ]);
+ });
+ // `append("each")`, `append("describe each")` and `append("three")` undone
+ test("four", async () => {
+ await append("four");
+ expect(await get()).toStrictEqual([
+ "all", "describe all", "each", "describe each", "four"
+ ]);
+ });
+ // `append("each")`, `append("describe each")` and `append("four")` undone
+ });
+ ```
+
+
+
+- {{}}singleWorker{{}}: {{}}boolean{{}}{{}}optional{{}}
+
+ - Runs all tests in this project serially in the same Worker, using the same module cache. This can significantly speed up execution if you have lots of small test files. Refer to the [Isolation and concurrency](/workers/testing/vitest-integration/isolation-and-concurrency/) page for more information on the isolation model.
+
+ - Defaults to `false`.
+
+- {{}}miniflare{{}}: {{}}SourcelessWorkerOptions & { workers?: WorkerOptions[]; }{{}}{{}}optional{{}}
+
+ - Use this to provide configuration information that is typically stored within the Wrangler configuration file, such as [bindings](/workers/configuration/bindings/), [compatibility dates](/workers/configuration/compatibility-dates/), and [compatibility flags](/workers/configuration/compatibility-dates/#compatibility-flags). The `WorkerOptions` interface is defined [here](https://github.com/cloudflare/workers-sdk/tree/main/packages/miniflare#interface-workeroptions). Use the `main` option above to configure the entry point, instead of the Miniflare `script`, `scriptPath`, or `modules` options.
+
+ - If your project makes use of multiple Workers, you can configure auxiliary Workers that run in the same `workerd` process as your tests and can be bound to. Auxiliary Workers are configured using the `workers` array, containing regular Miniflare [`WorkerOptions`](https://github.com/cloudflare/workers-sdk/tree/main/packages/miniflare#interface-workeroptions) objects. Note that unlike the `main` Worker, auxiliary Workers:
+ - Cannot have TypeScript entrypoints. You must compile auxiliary Workers to JavaScript first. You can use the [`wrangler deploy --dry-run --outdir dist`](/workers/wrangler/commands/#deploy) command for this.
+ - Use regular Workers module resolution semantics. Refer to the [Isolation and concurrency](/workers/testing/vitest-integration/isolation-and-concurrency/#modules) page for more information.
+ - Cannot access the [`cloudflare:test`](/workers/testing/vitest-integration/test-apis/) module.
+ - Do not require specific compatibility dates or flags.
+ - Can be written with the [Service Worker syntax](/workers/reference/migrate-to-module-workers/#service-worker-syntax).
+ - Are not affected by global mocks defined in your tests.
+
+- {{}}wrangler{{}}: {{}}{ configPath?: string; }{{}}{{}}optional{{}}
+
+ - Path to Wrangler configuration file to load `main`, [compatibility settings](/workers/configuration/compatibility-dates/) and [bindings](/workers/configuration/bindings/) from. These options will be merged with the `miniflare` option above, with `miniflare` values taking precedence. For example, if your Wrangler configuration defined a [service binding](/workers/configuration/bindings/about-service-bindings/) named `SERVICE` to a Worker named `service`, but you included `serviceBindings: { SERVICE(request) { return new Response("body"); } }` in the `miniflare` option, all requests to `SERVICE` in tests would return `body`. Note `configPath` accepts both `.toml` and `.json` files.
+
+{{}}
+
+{{}}
+
+## `WorkersPoolOptionsContext`
+
+{{}}
+
+- {{}}inject{{}}: {{}}typeof import("vitest").inject{{}}
+
+ - The same `inject()` function usually imported from the `vitest` module inside tests. This allows you to define `miniflare` configuration based on injected values from [`globalSetup`](https://vitest.dev/config/#globalsetup) scripts. Use this if you have a value in your configuration that is dynamically generated and only known at runtime of your tests. For example, a global setup script might start an upstream server on a random port. This port could be `provide()`d and then `inject()`ed in the configuration for an external service binding or [Hyperdrive](/hyperdrive/). Refer to the [Hyperdrive recipe](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/hyperdrive) for an example project using this provide/inject approach.
+
+
+ Illustrative example
+
+ ```ts
+ // env.d.ts
+ declare module "vitest" {
+ interface ProvidedContext {
+ port: number;
+ }
+ }
+
+ // global-setup.ts
+ import type { GlobalSetupContext } from "vitest/node";
+ export default function ({ provide }: GlobalSetupContext) {
+ // Runs inside Node.js, could start server here...
+ provide("port", 1337);
+ return () => { /* ...then teardown here */ };
+ }
+
+ // vitest.config.ts
+ import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
+ export default defineWorkersConfig({
+ test: {
+ globalSetup: ["./global-setup.ts"],
+ pool: "@cloudflare/vitest-pool-workers",
+ poolOptions: {
+ workers: ({ inject }) => ({
+ miniflare: {
+ hyperdrives: {
+ DATABASE: `postgres://user:pass@example.com:${inject("port")}/db`,
+ },
+ },
+ }),
+ },
+ },
+ });
+ ```
+
+
+
+{{}}
+
+## `SourcelessWorkerOptions`
+
+Sourceless `WorkerOptions` type without `script`, `scriptPath`, or `modules` properties. Refer to the Miniflare [`WorkerOptions`](https://github.com/cloudflare/workers-sdk/tree/main/packages/miniflare#interface-workeroptions) type for more details.
+
+```ts
+type SourcelessWorkerOptions = Omit<
+ WorkerOptions,
+ "script" | "scriptPath" | "modules" | "modulesRoot"
+>;
+```
diff --git a/content/workers/testing/vitest-integration/get-started/_index.md b/content/workers/testing/vitest-integration/get-started/_index.md
new file mode 100644
index 00000000000000..4f82f9ee00e5de
--- /dev/null
+++ b/content/workers/testing/vitest-integration/get-started/_index.md
@@ -0,0 +1,61 @@
+---
+pcx_content_type: navigation
+title: Get started
+weight: 1
+meta:
+ description: Install and set up the Workers Vitest integration - a tool for writing unit and integration tests for Workers and Pages Functions.
+---
+
+# Get started
+
+{{}}
+
+---
+
+For most users, Cloudflare recommends using the Workers Vitest integration for testing Workers and [Pages Functions](/pages/functions/) projects. [Vitest](https://vitest.dev/) is a popular JavaScript testing framework featuring a very fast watch mode, Jest compatibility, and out-of-the-box support for TypeScript. In this integration, Cloudflare provides a custom pool that allows your Vitest tests to run _inside_ the Workers runtime.
+
+Get started with the [Vitest integration Get started guide](/workers/testing/vitest-integration/get-started/write-your-first-test/), and refer to [Recipes for testing different types of Workers](/workers/testing/vitest-integration/recipes/).
+
+The Workers Vitest integration...
+
+- Supports both **unit tests** and **integration tests**.
+- Provides direct access to Workers runtime APIs and bindings.
+- Implements isolated per-test storage.
+- Runs tests fully-locally using [Miniflare](https://miniflare.dev/).
+- Leverages Vitest's hot-module reloading for near instant reruns.
+- Provides a declarative interface for mocking outbound requests.
+- Supports projects with multiple Workers.
+
+{{}}
+
+Here's an example of a unit-style and integration-style test using the integration:
+
+```js
+import {
+ env,
+ SELF,
+ createExecutionContext,
+ waitOnExecutionContext,
+} from "cloudflare:test";
+import { it, expect } from "vitest";
+import worker, { add } from "./index.mjs";
+
+it("adds via function call", () => {
+ expect(add(1, 2)).toBe(3);
+});
+it("adds via request (unit style)", async () => {
+ const request = new Request("http://example.com/?a=1&b=2");
+ const ctx = createExecutionContext();
+ const response = await worker.fetch(request, env, ctx);
+ await waitOnExecutionContext(ctx);
+ expect(await response.text()).toBe("3");
+});
+it("adds via request (integration style)", async () => {
+ const response = await SELF.fetch("http://example.com/?a=1&b=2");
+ expect(await response.text()).toBe("3");
+});
+```
\ No newline at end of file
diff --git a/content/workers/testing/vitest-integration/get-started/migrate-from-miniflare-2.md b/content/workers/testing/vitest-integration/get-started/migrate-from-miniflare-2.md
new file mode 100644
index 00000000000000..0c83a64270139f
--- /dev/null
+++ b/content/workers/testing/vitest-integration/get-started/migrate-from-miniflare-2.md
@@ -0,0 +1,236 @@
+---
+title: Migrate from Miniflare 2's test environments
+weight: 2
+pcx_content_type: concept
+meta:
+ description: Migrate from [Miniflare 2](https://github.com/cloudflare/miniflare?tab=readme-ov-file) to the Workers Vitest integration.
+---
+
+# Migrate from Miniflare 2's test environments
+
+[Miniflare 2](https://github.com/cloudflare/miniflare?tab=readme-ov-file) provided custom environments for Jest and Vitest in the `jest-environment-miniflare` and `vitest-environment-miniflare` packages respectively.
+The `@cloudflare/vitest-pool-workers` package provides similar functionality using modern Miniflare versions and the [`workerd` runtime](https://github.com/cloudflare/workerd). `workerd` is the same JavaScript/WebAssembly runtime that powers Cloudflare Workers. Using `workerd` practically eliminates behavior mismatches between your tests and deployed code. Refer to the [Miniflare 3 announcement](https://blog.cloudflare.com/miniflare-and-workerd) for more information.
+
+{{}}
+
+{{}}
+
+## Install the Workers Vitest integration
+
+First, you will need to uninstall the old environment and install the new pool. Vitest environments can only customize the global scope, whereas pools can run tests using a completely different runtime. In this case, the pool runs your tests inside [`workerd`](https://github.com/cloudflare/workerd) instead of Node.js.
+
+```sh
+$ npm uninstall vitest-environment-miniflare
+$ npm install --save-dev --save-exact vitest@1.3.0
+$ npm install --save-dev @cloudflare/vitest-pool-workers
+```
+
+## Update your Vitest configuration file
+
+After installing the Workers Vitest configuration, update your Vitest configuration file to use the pool instead. Most Miniflare configuration previously specified `environmentOptions` can be moved to `poolOptions.workers.miniflare` instead. Refer to [Miniflare's `WorkerOptions` interface](https://github.com/cloudflare/workers-sdk/blob/main/packages/miniflare/README.md#interface-workeroptions) for supported options and the [Miniflare version 2 to 3 migration guide](https://miniflare.dev/get-started/migrating#api-changes) for more information. If you relied on configuration stored in a `wrangler.toml` file, set `wrangler.configPath` too.
+
+```diff
+---
+filename: vitest.config.js
+---
++ import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
+
+ export default defineWorkersConfig({
+ test: {
+- environment: "miniflare",
+- environmentOptions: { ... },
++ poolOptions: {
++ workers: {
++ miniflare: { ... },
++ wrangler: { configPath: "./wrangler.toml" },
++ },
++ },
+ },
+ });
+```
+
+## Update your TypeScript configuration file
+
+If you are using TypeScript, update your `tsconfig.json` to include the correct ambient `types`:
+
+```diff
+---
+filename: tsconfig.json
+---
+ {
+ "compilerOptions": {
+ ...,
+ "types": [
+ "@cloudflare/workers-types/experimental"
+- "vitest-environment-miniflare/globals"
++ "@cloudflare/vitest-pool-workers"
+ ]
+ },
+ }
+```
+
+## Access bindings
+
+To access [bindings](/workers/runtime-apis/bindings/) in your tests, use the `env` helper from the `cloudflare:test` module.
+
+```diff
+---
+filename: index.spec.js
+---
+ import { it } from "vitest";
++ import { env } from "cloudflare:test";
+
+ it("does something", () => {
+- const env = getMiniflareBindings();
+ // ...
+ });
+```
+
+If you are using TypeScript, add an ambient `.d.ts` declaration file defining a `ProvidedEnv` `interface` in the `cloudflare:test` module to control the type of `env`:
+
+```ts
+---
+filename: env.d.ts
+---
+declare module "cloudflare:test" {
+ interface ProvidedEnv {
+ NAMESPACE: KVNamespace;
+ }
+ // ...or if you have an existing `Env` type...
+ interface ProvidedEnv extends Env {}
+}
+```
+
+## Use isolated storage
+
+Isolated storage is now enabled by default. You no longer need to include `setupMiniflareIsolatedStorage()` in your tests.
+
+```diff
+---
+filename: index.spec.js
+---
+- const describe = setupMiniflareIsolatedStorage();
++ import { describe } from "vitest";
+```
+
+## Work with `waitUntil()`
+
+The `new ExecutionContext()` constructor and `getMiniflareWaitUntil()` function are now `createExecutionContext()` and `waitOnExecutionContext()` respectively. Note `waitOnExecutionContext()` now returns an empty `Promise` instead of a `Promise` resolving to the results of all `waitUntil()`ed `Promise`s.
+
+```diff
+---
+filename: index.spec.js
+---
++ import { createExecutionContext, waitOnExecutionContext } from "cloudflare:test";
+
+ it("does something", () => {
+ // ...
+- const ctx = new ExecutionContext();
++ const ctx = createExecutionContext();
+ const response = worker.fetch(request, env, ctx);
+- await getMiniflareWaitUntil(ctx);
++ await waitOnExecutionContext(ctx);
+ });
+```
+
+## Mock outbound requests
+
+The `getMiniflareFetchMock()` function has been replaced with the new `fetchMock` helper from the `cloudflare:test` module. `fetchMock` has the same type as the return type of `getMiniflareFetchMock()`. There are a couple of differences between `fetchMock` and the previous return value of `getMiniflareFetchMock()`:
+
+- `fetchMock` is deactivated by default, whereas previously it would start activated. This deactivation prevents unnecessary buffering of request bodies if you are not using `fetchMock`. You will need to call `fetchMock.activate()` before calling `fetch()` to enable it.
+- `fetchMock` is reset at the start of each test run, whereas previously, interceptors added in previous runs would apply to the current one. This ensures test runs are not affected by previous runs.
+
+```diff
+---
+filename: index.spec.js
+---
+ import { beforeAll, afterAll } from "vitest";
++ import { fetchMock } from "cloudflare:test";
+
+- const fetchMock = getMiniflareFetchMock();
+ beforeAll(() => {
++ fetchMock.activate();
+ fetchMock.disableNetConnect();
+ fetchMock
+ .get("https://example.com")
+ .intercept({ path: "/" })
+ .reply(200, "data");
+ });
+ afterAll(() => fetchMock.assertNoPendingInterceptors());
+```
+
+## Use Durable Object helpers
+
+The `getMiniflareDurableObjectStorage()`, `getMiniflareDurableObjectState()`, `getMiniflareDurableObjectInstance()`, and `runWithMiniflareDurableObjectGates()` functions have all been replaced with a single `runInDurableObject()` function from the `cloudflare:test` module. The `runInDurableObject()` function accepts a `DurableObjectStub` with a callback accepting the Durable Object instance and corresponding `DurableObjectState` as arguments. Consolidating these functions into a single function simplifies the API surface, and ensures instances are accessed with the correct request context and [gating behavior](https://blog.cloudflare.com/durable-objects-easy-fast-correct-choose-three/). Refer to the [Test APIs page](/workers/testing/vitest-integration/test-apis/) for more details.
+
+```diff
+---
+filename: index.spec.js
+---
++ import { env, runInDurableObject } from "cloudflare:test";
+
+ it("does something", async () => {
+- const env = getMiniflareBindings();
+ const id = env.OBJECT.newUniqueId();
++ const stub = env.OBJECT.get(id);
+
+- const storage = await getMiniflareDurableObjectStorage(id);
+- doSomethingWith(storage);
++ await runInDurableObject(stub, async (instance, state) => {
++ doSomethingWith(state.storage);
++ });
+
+- const state = await getMiniflareDurableObjectState(id);
+- doSomethingWith(state);
++ await runInDurableObject(stub, async (instance, state) => {
++ doSomethingWith(state);
++ });
+
+- const instance = await getMiniflareDurableObjectInstance(id);
+- await runWithMiniflareDurableObjectGates(state, async () => {
+- doSomethingWith(instance);
+- });
++ await runInDurableObject(stub, async (instance) => {
++ doSomethingWith(instance);
++ });
+ });
+```
+
+The `flushMiniflareDurableObjectAlarms()` function has been replaced with the `runDurableObjectAlarm()` function from the `cloudflare:test` module. The `runDurableObjectAlarm()` function accepts a single `DurableObjectStub` and returns a `Promise` that resolves to `true` if an alarm was scheduled and the `alarm()` handler was executed, or `false` otherwise. To "flush" multiple instances' alarms, call `runDurableObjectAlarm()` in a loop.
+
+```diff
+---
+filename: index.spec.js
+---
++ import { env, runDurableObjectAlarm } from "cloudflare:test";
+
+ it("does something", async () => {
+- const env = getMiniflareBindings();
+ const id = env.OBJECT.newUniqueId();
+- await flushMiniflareDurableObjectAlarms([id]);
++ const stub = env.OBJECT.get(id);
++ const ran = await runDurableObjectAlarm(stub);
+ });
+```
+
+Finally, the `getMiniflareDurableObjectIds()` function has been replaced with the `listDurableObjectIds()` function from the `cloudflare:test` module. The `listDurableObjectIds()` function now accepts a `DurableObjectNamespace` instance instead of a namespace `string` to provide stricter typing. Note the `listDurableObjectIds()` function now respects isolated storage. If enabled, IDs of objects created in other tests will not be returned.
+
+```diff
+---
+filename: index.spec.js
+---
++ import { env, listDurableObjectIds } from "cloudflare:test";
+
+ it("does something", async () => {
+- const ids = await getMiniflareDurableObjectIds("OBJECT");
++ const ids = await listDurableObjectIds(env.OBJECT);
+ });
+```
\ No newline at end of file
diff --git a/content/workers/testing/vitest-integration/get-started/migrate-from-unstable-dev.md b/content/workers/testing/vitest-integration/get-started/migrate-from-unstable-dev.md
new file mode 100644
index 00000000000000..a8280594dd0e75
--- /dev/null
+++ b/content/workers/testing/vitest-integration/get-started/migrate-from-unstable-dev.md
@@ -0,0 +1,109 @@
+---
+title: Migrate from unstable_dev
+weight: 3
+pcx_content_type: concept
+meta:
+ description: Migrate from the [`unstable_dev`](/workers/wrangler/api/#unstable_dev) API to writing tests with the Workers Vitest integration.
+---
+
+# Migrate from `unstable_dev`
+
+The [`unstable_dev`](/workers/wrangler/api/#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, all whilst providing isolated per-test storage.
+
+This guide demonstrates key differences between tests written with the `unstable_dev` API and the Workers Vitest integration. For more information on writing tests with the Workers Vitest integration, refer to [Write your first test](/workers/testing/vitest-integration/get-started/write-your-first-test/).
+
+## Reference a Worker for integration testing
+
+With `unstable_dev`, to trigger a `fetch` event, you would do this:
+
+```js
+---
+filename: index.spec.js
+---
+import { unstable_dev } from "wrangler"
+
+it("dispatches fetch event", () => {
+ const worker = await unstable_dev("src/index.ts");
+ const resp = await worker.fetch("http://example.com");
+ ...
+})
+```
+
+With the Workers Vitest integration, you can accomplish the same goal using `SELF` from `cloudflare:test`. `SELF` is a [service binding](/workers/runtime-apis/bindings/service-bindings/) to the default export defined by the `main` option in your `wrangler.toml`. This `main` Worker runs in the same isolate as tests so any global mocks will apply to it too.
+
+```js
+---
+filename: index.spec.ts
+---
+import { SELF } from "cloudflare:test";
+import "../src/"; // Currently required to automatically rerun tests when `main` changes
+
+it("dispatches fetch event", async () => {
+ const response = await SELF.fetch("http://example.com");
+ ...
+});
+```
+
+## Stop a Worker
+
+With the Workers Vitest integration, there is no need to stop a Worker via `worker.stop()`. This functionality is handled automatically after tests run.
+
+## Import Wrangler configuration
+
+Via the `unstable_dev` API, you can reference a `wrangler.toml` configuration file by adding it as an option:
+
+```js
+---
+filename: index.spec.ts
+---
+await unstable_dev("src/index.ts", {
+ config: "wrangler.toml",
+});
+```
+
+With the Workers Vitest integration, you can now set this reference to `wrangler.toml` in `vitest.config.js` for all of your tests:
+
+```js
+---
+filename: vitest.config.js
+highlight: [5-7]
+---
+export default defineWorkersConfig({
+ test: {
+ poolOptions: {
+ workers: {
+ wrangler: {
+ configPath: "wrangler.toml",
+ },
+ },
+ },
+ },
+});
+---
+```
+
+## Test service Workers
+
+Unlike the `unstable_dev` API, the Workers Vitest integration does not support testing Workers using the service worker format. You will need to first [migrate to the ES modules format](/workers/reference/migrate-to-module-workers/) in order to use the Workers Vitest integration.
+
+## Define types
+
+You can remove `UnstableDevWorker` imports from your code. Instead, follow the [Write your first test guide](/workers/testing/vitest-integration/get-started/write-your-first-test/#define-types) to define types for all of your tests.
+
+```diff
+---
+filename: index.spec.ts
+---
+- import { unstable_dev } from "wrangler";
+- import type { UnstableDevWorker } from "wrangler";
++ import worker from "src/index.ts";
+
+ describe("Worker", () => {
+- let worker: UnstableDevWorker;
+ ...
+ });
+```
+
+## Related resources
+
+- [Write your first test](/workers/testing/vitest-integration/get-started/write-your-first-test/#define-types) - Write unit tests against Workers.
\ No newline at end of file
diff --git a/content/workers/testing/vitest-integration/get-started/write-your-first-test.md b/content/workers/testing/vitest-integration/get-started/write-your-first-test.md
new file mode 100644
index 00000000000000..95b7a621d9f503
--- /dev/null
+++ b/content/workers/testing/vitest-integration/get-started/write-your-first-test.md
@@ -0,0 +1,288 @@
+---
+title: Write your first test
+weight: 1
+pcx_content_type: concept
+meta:
+ description: Write unit tests against Workers.
+---
+
+# Write your first test
+
+This guide will guide you through install and setup of the `@cloudflare/vitest-pool-workers` package, and 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](https://nodejs.org/api/worker_threads.html). For example of tests `@cloudflare/vitest-pool-workers`, refer to the [Recipes](/workers/testing/vitest-integration/recipes/) page.
+
+## Prerequisites
+
+- Open the root directory of your Worker or [create a new Worker](/workers/get-started/guide/#1-create-a-new-worker-project)
+
+- In your project's `wrangler.toml` configuration file, define a [compatibility date](/workers/configuration/compatibility-dates/) of `2022-10-31` or higher, and include `nodejs_compat` in your [compatibility flags](/workers/wrangler/configuration/#use-runtime-apis-directly).
+
+## Install Vitest and `@cloudflare/vitest-pool-workers`
+
+Open a terminal window and make sure you are in your project's root directory. Once you have confirmed that, run:
+
+```sh
+$ npm install vitest@1.3.0 --save-dev --save-exact
+$ npm 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.
+
+{{}}
+
+## Define Vitest configuration
+
+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.toml` file to leverage its `main` entry point, [compatibility settings](/workers/configuration/compatibility-dates/), and [bindings](/workers/configuration/bindings/).
+
+```js
+---
+filename: vitest.config.js
+---
+import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
+
+export default defineWorkersConfig({
+ test: {
+ poolOptions: {
+ workers: {
+ wrangler: { configPath: "./wrangler.toml" },
+ },
+ },
+ },
+});
+```
+
+{{}}
+
+### Add configuration options via Miniflare
+
+Under the hood, the Workers Vitest integration uses [Miniflare](https://miniflare.dev), the same simulator that powers [`wrangler dev`'s](/workers/wrangler/commands/#dev) 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`:
+
+```js
+---
+filename: vitest.config.js
+highlight: [6-8]
+---
+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.
+
+{{}}
+
+## Define types
+
+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:
+
+```js
+---
+filename: tsconfig.json
+---
+{
+ "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.
+
+## Write tests
+
+If you created a basic Worker via the guide listed above, you should have the following fetch handler in the `src` folder:
+
+{{}}
+{{}}
+```js
+---
+filename: src/index.js
+---
+export default {
+ async fetch(request, env, ctx) {
+ return new Response("Hello World!");
+ },
+};
+```
+{{}}
+{{}}
+```ts
+---
+filename: src/index.ts
+---
+export default {
+ async fetch(request, env, ctx) {
+ return new Response("Hello World!");
+ },
+} satisfies ExportedHandler;
+```
+{{}}
+{{}}
+
+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:
+
+{{}}
+{{}}
+```js
+---
+filename: test/index.spec.js
+---
+import { env, createExecutionContext, waitOnExecutionContext } from "cloudflare:test";
+import { describe, it, expect } from "vitest";
+// Could import any other source file/function here
+import 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!");
+ });
+});
+```
+{{}}
+{{}}
+```ts
+---
+filename: test/index.spec.ts
+---
+import { env, createExecutionContext, waitOnExecutionContext } from "cloudflare:test";
+import { describe, it, expect } from "vitest";
+// Could import any other source file/function here
+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 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`.
+
+{{}}
+{{}}
+```js
+---
+filename: index.js
+---
+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!");
+ },
+};
+```
+{{}}
+{{}}
+```ts
+---
+filename: index.ts
+---
+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!");
+ }
+} satisfies ExportedHandler;
+```
+{{}}
+{{}}
+
+To test this, add the following to your test file:
+
+{{}}
+{{}}
+```js
+---
+filename: index.spec.js
+---
+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");
+});
+```
+{{}}
+{{}}
+```ts
+---
+filename: index.spec.ts
+---
+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");
+});
+```
+{{}}
+{{}}
+
+## Related resources
+
+- [`@cloudflare/vitest-pool-workers` GitHub repository](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples) - Examples of tests using the `@cloudflare/vitest-pool-workers` package.
\ No newline at end of file
diff --git a/content/workers/testing/vitest-integration/isolation-and-concurrency.md b/content/workers/testing/vitest-integration/isolation-and-concurrency.md
new file mode 100644
index 00000000000000..4977cad9703b99
--- /dev/null
+++ b/content/workers/testing/vitest-integration/isolation-and-concurrency.md
@@ -0,0 +1,62 @@
+---
+title: Isolation and concurrency
+pcx_content_type: concept
+weight: 6
+---
+
+# Isolation and concurrency
+
+Review how the Workers Vitest integration runs your tests, how it isolates tests from each other, and how it imports modules.
+
+## Run tests
+
+When you run your tests with the Workers Vitest integration, Vitest will:
+
+1. Read and evaluate your configuration file using Node.js.
+2. Run any [`globalSetup`](https://vitest.dev/config/#globalsetup) files using Node.js.
+3. Collect and sequence test files.
+4. For each Vitest project, depending on its configured isolation and concurrency, start one or more [`workerd`](https://github.com/cloudflare/workerd) processes, each running one or more Workers.
+5. Run [`setupFiles`](https://vitest.dev/config/#setupfiles) and test files in `workerd` using the appropriate Workers.
+6. Watch for changes and re-run test files using the same Workers if the configuration has not changed.
+
+## Isolation and concurrency models
+
+The [`isolatedStorage` and `singleWorker`](/workers/testing/vitest-integration/configuration/#workerspooloptions-definition) configuration options both control isolation and concurrency. The Workers Vitest integration tries to minimise the number of `workerd` processes it starts, reusing Workers and their module caches between test runs where possible. The current implementation of isolated storage requires each `workerd` process to run one test file at a time, and does not support `.concurrent` tests. A copy of all auxiliary `workers` exists in each `workerd` process.
+
+By default, the `isolatedStorage` option is enabled. We recommend you enable the `singleWorker: true` option if you have lots of small test files.
+
+### `isolatedStorage: true, singleWorker: false` (Default)
+
+In this model, a `workerd` process is started for each test file. Test files are executed concurrently but `.concurrent` tests are not supported. Each test will read/write from an isolated storage environment, and bind to its own set of auxiliary `workers`.
+
+![Isolation Model: Isolated Storage & No Single Worker](/images/workers/testing/vitest/isolation-model-3-isolated-storage-no-single-worker.svg)
+
+### `isolatedStorage: true, singleWorker: true`
+
+In this model, a single `workerd` process is started with a single Worker for all test files. Test files are executed in serial and `.concurrent` tests are not supported. Each test will read/write from an isolated storage environment, and bind to the same auxiliary `workers`.
+
+![Isolation Model: Isolated Storage & Single Worker](/images/workers/testing/vitest/isolation-model-4-isolated-storage-single-worker.svg)
+
+### `isolatedStorage: false, singleWorker: false`
+
+In this model, a single `workerd` process is started with a Worker for each test file. Tests files are executed concurrently and `.concurrent` tests are supported. Every test will read/write from the same shared storage, and bind to the same auxiliary `workers`.
+
+![Isolation Model: No Isolated Storage & No Single Worker](/images/workers/testing/vitest/isolation-model-1-no-isolated-storage-no-single-worker.svg)
+
+### `isolatedStorage: false, singleWorker: true`
+
+In this model, a single `workerd` process is started with a single Worker for all test files. Test files are executed in serial but `.concurrent` tests are supported. Every test will read/write from the same shared storage, and bind to the same auxiliary `workers`.
+
+![Isolation Model: No Isolated Storage & Single Worker](/images/workers/testing/vitest/isolation-model-2-no-isolated-storage-single-worker.svg)
+
+## Modules
+
+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.
+
+The Workers Vitest pool works by running code inside a Cloudflare Worker that Vitest would usually run inside a [Node.js worker thread](https://nodejs.org/api/worker_threads.html). To make this possible, the pool requires the [`nodejs_compat`](/workers/configuration/compatibility-dates/#nodejs-compatibility-flag) and [`export_commonjs_default`](/workers/configuration/compatibility-dates/#commonjs-modules-do-not-export-a-module-namespace) compatibility flags to be enabled. The pool also configures `workerd` to use Node-style module resolution and polyfills required `node:*` modules not provided by `nodejs_compat`.
+
+{{}}
diff --git a/content/workers/testing/vitest-integration/known-issues.md b/content/workers/testing/vitest-integration/known-issues.md
new file mode 100644
index 00000000000000..dbe311d0d6e7c4
--- /dev/null
+++ b/content/workers/testing/vitest-integration/known-issues.md
@@ -0,0 +1,14 @@
+---
+title: Known issues
+pcx_content_type: concept
+weight: 7
+---
+
+# Known issues
+
+The Workers Vitest pool is currently in open-beta. These are issues Cloudflare is aware of and fixing:
+
+- Dynamic `import()` statements do not work inside `export default { ... }` handlers when writing integration tests with `SELF`, or inside Durable Object event handlers. You must use static `import` statements in the global scope.
+- `console.log()`s inside `export default { ... }` handlers are not shown when writing integration tests with `SELF` if the handler does no asynchronous work. You can work around this by including `ctx.waitUntil(scheduler.wait(100))` in your tests during debugging to keep the request context alive for long enough.
+- If you are writing integration tests with `SELF`, you must import your Worker's `main` entrypoint in your test file for tests to re-run when files change. For example, if `main` was set to `./src/index.ts`, include `import "./src/index"` at the top of each test file. Vite's module analysis that powers hot-module-reloading is performed statically and currently does not detect the dynamic import of `main` in our test runner.
+- Durable Object alarms are not reset between test runs and do not respect isolated storage. Ensure you delete or run all alarms scheduled in each test before finishing the test.
\ No newline at end of file
diff --git a/content/workers/testing/vitest-integration/recipes.md b/content/workers/testing/vitest-integration/recipes.md
new file mode 100644
index 00000000000000..67be044ecb2d7d
--- /dev/null
+++ b/content/workers/testing/vitest-integration/recipes.md
@@ -0,0 +1,20 @@
+---
+pcx_content_type: concept
+title: Recipes
+weight: 3
+---
+
+# Recipes
+
+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 using `SELF`](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/basics-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)
+- [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)
+- [Tests using Queue producers and consumers](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/queues)
+- [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 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)
diff --git a/content/workers/testing/vitest-integration/test-apis.md b/content/workers/testing/vitest-integration/test-apis.md
new file mode 100644
index 00000000000000..e33c4a93cd7638
--- /dev/null
+++ b/content/workers/testing/vitest-integration/test-apis.md
@@ -0,0 +1,289 @@
+---
+title: Test APIs
+pcx_content_type: reference
+weight: 5
+---
+
+# Test APIs
+
+The Workers Vitest integration provides runtime helpers for writing tests in the `cloudflare:test` module. The `cloudflare:test` module is provided by the `@cloudflare/vitest-pool-workers` package, but can only be imported from test files that execute in the Workers runtime.
+
+## `cloudflare:test` module definition
+
+{{}}
+
+- {{}}env{{}}: {{}}import("cloudflare:test").ProvidedEnv{{}}
+
+ - Exposes the [`env` object](/workers/runtime-apis/handlers/fetch/#parameters) for use as the second argument passed to ES modules format exported handlers. This provides access to [bindings](/workers/runtime-apis/bindings/) that you have defined in your [Vitest configuration file](/workers/testing/vitest-integration/configuration/).
+
+
+
+ ```js
+ ---
+ filename: index.spec.js
+ ---
+ import { env } from "cloudflare:test";
+
+ 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
+ ---
+ filename: env.d.ts
+ ---
+ declare module "cloudflare:test" {
+ interface ProvidedEnv {
+ KV_NAMESPACE: KVNamespace;
+ }
+ // ...or if you have an existing `Env` type...
+ interface ProvidedEnv extends Env {}
+ }
+ ```
+
+- {{}}SELF{{}}: {{}}Fetcher{{}}
+
+ - [Service binding](/workers/runtime-apis/bindings/service-bindings/) to the default export defined in the `main` Worker. Use this to write integration tests against your Worker. The `main` Worker runs in the same isolate/context as tests so any global mocks will apply to it too.
+
+
+
+ ```js
+ ---
+ filename: index.spec.js
+ ---
+ import { SELF } from "cloudflare:test";
+
+ it("dispatches fetch event", async () => {
+ const response = await SELF.fetch("https://example.com");
+ expect(await response.text()).toMatchInlineSnapshot(...);
+ });
+ ```
+
+- {{}}fetchMock{{}}: {{}}import("undici").MockAgent{{}}
+
+ - Declarative interface for mocking outbound `fetch()` requests. Deactivated by default and reset before running each test file. Refer to [`undici`'s `MockAgent` documentation](https://undici.nodejs.org/#/docs/api/MockAgent) for more information. Note this only mocks `fetch()` requests for the current test runner Worker. Auxiliary Workers should mock `fetch()`es using the Miniflare `fetchMock`/`outboundService` options. Refer to [Configuration](/workers/testing/vitest-integration/configuration/#workerspooloptions) for more information.
+
+
+
+ ```js
+ ---
+ filename: index.spec.js
+ ---
+ import { fetchMock } from "cloudflare:test";
+ import { beforeAll, afterEach, it, expect } from "vitest";
+
+ beforeAll(() => {
+ // Enable outbound request mocking...
+ fetchMock.activate();
+ // ...and throw errors if an outbound request isn't mocked
+ fetchMock.disableNetConnect();
+ });
+ // Ensure we matched every mock we defined
+ afterEach(() => fetchMock.assertNoPendingInterceptors());
+
+ it("mocks requests", async () => {
+ // Mock the first request to `https://example.com`
+ fetchMock
+ .get("https://example.com")
+ .intercept({ path: "/" })
+ .reply(200, "body");
+
+ const response = await fetch("https://example.com/");
+ expect(await response.text()).toBe("body");
+ });
+ ```
+
+{{}}
+
+### Events
+
+{{}}
+
+- {{}}createExecutionContext(){{}}: {{}}ExecutionContext{{}}
+
+ - Creates an instance of the [`context` object](/workers/runtime-apis/handlers/fetch/#parameters) for use as the third argument to ES modules format exported handlers.
+
+- {{}}waitOnExecutionContext(ctx:{{}}ExecutionContext{{}}){{}}: {{}}Promise\{{}}
+
+ - 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
+ ---
+ filename: index.spec.js
+ ---
+ import { env, 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()`](/workers/runtime-apis/handlers/scheduled/) exported handlers.
+
+
+
+ ```ts
+ ---
+ filename: index.spec.js
+ ---
+ import { env, 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()`](/queues/reference/javascript-apis/#consumer) exported handlers.
+
+- {{}}getQueueResult(batch:{{}}MessageBatch{{}}, ctx:{{}}ExecutionContext{{}}){{}}: {{}}Promise\{{}}
+
+ - Gets the acknowledged/retry state of messages in the `MessageBatch`, and waits for all `ExecutionContext#waitUntil()`ed `Promise`s to settle. Only accepts instances of `MessageBatch` returned by `createMessageBatch()`, and instances of `ExecutionContext` returned by `createExecutionContext()`.
+
+
+
+ ```ts
+ ---
+ filename: index.spec.js
+ ---
+ import { env, 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\(stub:{{}}DurableObjectStub{{}}, callback:{{}}(instance: O, state: DurableObjectState) => R | Promise\{{}}){{}}: {{}}Promise\{{}}
+
+ - Runs the provided `callback` inside the Durable Object instance 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 instance methods or seed/get persisted data. Note this can only be used with `stub`s pointing to Durable Objects defined in the `main` Worker.
+
+
+
+ ```ts
+ ---
+ filename: index.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
+ ---
+ filename: index.spec.ts
+ ---
+ import { env, 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\{{}}
+
+ - 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 `stub`s pointing to Durable Objects defined in the `main` Worker.
+
+- {{}}listDurableObjectIds(namespace:{{}}DurableObjectNamespace{{}}){{}}: {{}}Promise\{{}}
+
+ - Gets the IDs of all objects that have been created in the `namespace`. Respects `isolatedStorage` if enabled, meaning objects created in a different test will not be returned.
+
+
+
+ ```ts
+ ---
+ filename: index.spec.js
+ ---
+ import { env, 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);
+ });
+ ```
+
+{{}}
+
+### D1
+
+{{}}
+
+- {{}}applyD1Migrations(db:{{}}D1Database{{}}, migrations:{{}}D1Migration[]{{}}, migrationTableName?:{{}}string{{}}){{}}: {{}}Promise\{{}}
+
+ - Applies all un-applied [D1 migrations](/d1/reference/migrations/) stored in the `migrations` array to database `db`, recording migrations state in the `migrationsTableName` table. `migrationsTableName` defaults to `d1_migrations`. Call the [`readD1Migrations()`](/workers/testing/vitest-integration/configuration/#functions) function from the `@cloudflare/vitest-pool-workers/config` package inside Node.js to get the `migrations` array. Refer to the [D1 recipe](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/d1) for an example project using migrations.
+
+{{}}
\ No newline at end of file
diff --git a/content/workers/wrangler/commands.md b/content/workers/wrangler/commands.md
index b2fee1ff3f26b1..637ad438258bff 100644
--- a/content/workers/wrangler/commands.md
+++ b/content/workers/wrangler/commands.md
@@ -765,7 +765,7 @@ As of Wrangler v3.2.0, `wrangler dev` is supported by any Linux distributions pr
{{}}
-`wrangler dev` is a way to [locally test](/workers/observability/local-development-and-testing/) your Worker while developing. With `wrangler dev` running, send HTTP requests to `localhost:8787` and your Worker should execute as expected. You will also see `console.log` messages and exceptions appearing in your terminal.
+`wrangler dev` is a way to [locally test](/workers/testing/local-development/) your Worker while developing. With `wrangler dev` running, send HTTP requests to `localhost:8787` and your Worker should execute as expected. You will also see `console.log` messages and exceptions appearing in your terminal.
---
diff --git a/content/workers/wrangler/configuration.md b/content/workers/wrangler/configuration.md
index 5b435ae2a7b0e1..6ba81ba97690b5 100644
--- a/content/workers/wrangler/configuration.md
+++ b/content/workers/wrangler/configuration.md
@@ -148,7 +148,7 @@ At a minimum, the `name`, `main` and `compatibility_date` keys are required to d
### Usage model
-As of March 1, 2024 the [usage model](/workers/platform/pricing/#workers) configured in your Worker's `wrangler.toml` will be ignored. The [Standard](/workers/platform/pricing/#example-pricing-standard-usage-model) usage model applies.
+As of March 1, 2024 the [usage model](/workers/platform/pricing/#workers) configured in your Worker's `wrangler.toml` will be ignored. The [Standard](/workers/platform/pricing/#example-pricing-standard-usage-model) usage model applies.
Some Workers Enterprise customers maintain the ability to change usage models. Your usage model must be configured through the Cloudflare dashboard by going to **Workers & Pages** > select your Worker > **Settings** > **Usage Model**.
@@ -455,7 +455,7 @@ To bind D1 databases to your Worker, assign an array of the below object to the
{{}}
@@ -672,7 +672,7 @@ To bind KV namespaces to your Worker, assign an array of the below object to the
{{}}
@@ -801,7 +801,7 @@ To bind R2 buckets to your Worker, assign an array of the below object to the `r
{{}}
diff --git a/data/glossary/workers.yaml b/data/glossary/workers.yaml
index 9c4b376f85d06a..fcff958950a1bf 100644
--- a/data/glossary/workers.yaml
+++ b/data/glossary/workers.yaml
@@ -1,6 +1,10 @@
---
productName: Workers
entries:
+- term: Auxiliary Worker
+ general_definition: |-
+ A Worker created locally via the Workers Vitest integration that runs in a separate isolate to the test runner, with a different global scope.
+
- term: binding
general_definition: |-
[Bindings](/workers/configuration/bindings/) allow your Workers to interact with resources on the Cloudflare Developer Platform.
@@ -39,7 +43,7 @@ entries:
- term: environment variable
general_definition: |-
- [Environment variables](/workers/configuration/environment-variables/) are a type of binding that allow you to attach text strings or JSON values to your Worker.
+ [Environment variables](/workers/configuration/environment-variables/) are a type of binding that allow you to attach text strings or JSON values to your Worker.
- term: handler
general_definition: |-
@@ -71,13 +75,13 @@ entries:
- term: Queues
general_definition: |-
- [Queues](/queues/) integrates with Cloudflare Workers and enables you to build applications that can guarantee delivery.
+ [Queues](/queues/) integrates with Cloudflare Workers and enables you to build applications that can guarantee delivery.
associated_products:
- Queues
- term: rollback
general_definition: |-
- [Rollbacks](/workers/configuration/deployments/#rollbacks) are a way to deploy an older deployment to the Cloudflare global network.
+ [Rollbacks](/workers/configuration/deployments/#rollbacks) are a way to deploy an older deployment to the Cloudflare global network.
- term: R2
general_definition: |-
@@ -99,7 +103,7 @@ entries:
- term: Tail Worker
general_definition: |-
- A [Tail Worker](/workers/observability/logging/tail-workers/) receives information about the execution of other Workers (known as producer Workers), such as HTTP statuses, data passed to `console.log()` or uncaught exceptions.
+ A [Tail Worker](/workers/observability/logging/tail-workers/) receives information about the execution of other Workers (known as producer Workers), such as HTTP statuses, data passed to `console.log()` or uncaught exceptions.
- term: V8
general_definition: |-
@@ -119,4 +123,10 @@ entries:
- term: wrangler.toml
general_definition: |-
- The [configuration](/workers/wrangler/configuration/) file used to customize the development and deployment setup for a Worker or a Pages Function.
\ No newline at end of file
+ The [configuration](/workers/wrangler/configuration/) file used to customize the development and deployment setup for a Worker or a Pages Function.
+---
+productName: Workers
+entries:
+- term: auxiliary Worker
+ general_definition: |-
+ a Worker created locally via the Workers Vitest integration that runs in a separate isolate to the test runner, with a different global scope.
diff --git a/static/style.css b/static/style.css
index a3924bd6f9f81e..cba4a8e00f6110 100644
--- a/static/style.css
+++ b/static/style.css
@@ -3896,6 +3896,16 @@ blockquote .DocsMarkdown--header-anchor-positioner {
padding: 0 0.6em;
}
+.DocsMarkdown details summary + div.code-container {
+ padding: 0;
+ border-radius: 0;
+}
+
+.DocsMarkdown details summary + div.code-container .CodeBlock {
+ border-radius: 0;
+ margin-bottom: 0;
+}
+
.DocsMarkdown details[open] {
margin-bottom: 1.5em;
}