---
title: Isolation and concurrency · Cloudflare Workers docs
description: Review how the Workers Vitest integration runs your tests, how it
  isolates tests from each other, and how it imports modules.
lastUpdated: 2025-01-08T12:19:30.000Z
chatbotDeprioritize: false
source_url:
  html: https://developers.cloudflare.com/workers/testing/vitest-integration/isolation-and-concurrency/
  md: https://developers.cloudflare.com/workers/testing/vitest-integration/isolation-and-concurrency/index.md
---

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`](https://developers.cloudflare.com/workers/testing/vitest-integration/configuration/#workerspooloptions) 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](https://developers.cloudflare.com/_astro/isolation-model-3-isolated-storage-no-single-worker.DigZKXdc_t0LpD.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](https://developers.cloudflare.com/_astro/isolation-model-4-isolated-storage-single-worker.DVzBSzPO_f5qSq.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](https://developers.cloudflare.com/_astro/isolation-model-1-no-isolated-storage-no-single-worker.BFp0f7BV_f5qSq.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](https://developers.cloudflare.com/_astro/isolation-model-2-no-isolated-storage-single-worker.CA-pStER_f5qSq.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 **automatically injects** the [`nodejs_compat`](https://developers.cloudflare.com/workers/configuration/compatibility-flags/#nodejs-compatibility-flag), \[`no_nodejs_compat_v2`] and [`export_commonjs_default`](https://developers.cloudflare.com/workers/configuration/compatibility-flags/#commonjs-modules-do-not-export-a-module-namespace) compatibility flags. This is the minimal compatibility setup that still allows Vitest to run correctly, but without pulling in polyfills and globals that aren't required. If you already have a Node.js compatibility flag defined in your configuration, Vitest Pool Workers will not try to add those flags.

Warning

Using Vitest Pool Workers may cause your Worker to behave differently when deployed than during testing as the `nodejs_compat` flag is enabled by default. This means that Node.js-specific APIs and modules are available when running your tests. However, Cloudflare Workers do not support these Node.js APIs in the production environment unless you specify this flag in your Worker configuration.

If you do not have a `nodejs_compat` or `nodejs_compat_v2` flag in your configuration and you import a Node.js module in your Worker code, your tests may pass, but you will find that you will not be able to deploy this Worker, as the upload call (either via the REST API or via Wrangler) will throw an error.

However, if you use Node.js globals that are not supported by the runtime, your Worker upload will be successful, but you may see errors in production code. Let's create a contrived example to illustrate the issue.

The `wrangler.toml` does not specify either `nodejs_compat` or `nodejs_compat_v2`:

```toml
name = "test"
main = "src/index.ts"
compatibility_date = "2024-12-16"
# no nodejs_compat flags here
```

In our `src/index.ts` file, we use the `process` object, which is a Node.js global, unavailable in the Workerd runtime:

```typescript
export default {
  async fetch(request, env, ctx): Promise<Response> {
    process.env.TEST = 'test';
    return new Response(process.env.TEST);
  },
} satisfies ExportedHandler<Env>;
```

The test is a simple assertion that the Worker managed to use `process`.

```typescript
it('responds with "test"', async () => {
  const response = await SELF.fetch('https://example.com/');
  expect(await response.text()).toMatchInlineSnapshot(`"test"`);
});
```

Now, if we run `npm run test`, we see that the tests will *pass*:

```plaintext
 ✓ test/index.spec.ts (1)
   ✓ responds with "test"


 Test Files  1 passed (1)
      Tests  1 passed (1)
```

And we can run `wrangler dev` and `wrangler deploy` without issues. It *looks like* our code is fine. However, this code will fail in production as `process` is not available in the Workerd runtime.

To fix the issue, we either need to avoid using Node.js APIs, or add the `nodejs_compat` flag to our Wrangler configuration.
