Skip to content

Commit 4d55a02

Browse files
sheremet-vaAriPerkkiopaescuj
authoredDec 19, 2023
feat: add --no-isolate flag to improve performance, add documentation about performance (#4777)
Co-authored-by: Ari Perkkiö <ari.perkkio@gmail.com> Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch>
1 parent f43fdd8 commit 4d55a02

File tree

11 files changed

+195
-6
lines changed

11 files changed

+195
-6
lines changed
 

‎docs/config/index.md

+15
Original file line numberDiff line numberDiff line change
@@ -2066,3 +2066,18 @@ Tells fake timers to clear "native" (i.e. not fake) timers by delegating to thei
20662066
- **Version:** Since Vitest 1.1.0
20672067

20682068
Path to a [workspace](/guide/workspace) config file relative to [root](#root).
2069+
2070+
### isolate
2071+
2072+
- **Type:** `boolean`
2073+
- **Default:** `true`
2074+
- **CLI:** `--no-isolate`, `--isolate=false`
2075+
- **Version:** Since Vitest 1.1.0
2076+
2077+
Run tests in an isolated environment. This option has no effect on `vmThreads` pool.
2078+
2079+
Disabling this option might improve [performance](/guide/performance) if your code doesn't rely on side effects (which is usually true for projects with `node` environment).
2080+
2081+
::: note
2082+
You can disable isolation for specific pools by using [`poolOptions`](#pooloptions) property.
2083+
:::

‎docs/guide/cli.md

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ Run only [benchmark](https://vitest.dev/guide/features.html#benchmarking-experim
8181
| `--outputFile <filename/-s>` | Write test results to a file when the `--reporter=json` or `--reporter=junit` option is also specified <br /> Via [cac's dot notation] you can specify individual outputs for multiple reporters |
8282
| `--coverage` | Enable coverage report |
8383
| `--run` | Do not watch |
84+
| `--isolate` | Run every test file in isolation. To disable isolation, use --no-isolate (default: `true`) |
8485
| `--mode <name>` | Override Vite mode (default: `test`) |
8586
| `--workspace <path>` | Path to a workspace configuration file |
8687
| `--globals` | Inject APIs globally |

‎docs/guide/performance.md

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Performance
2+
3+
By default Vitest runs every test file in an isolated environment based on the [pool](/config/#pool):
4+
5+
- `threads` pool runs every test file in a separate [`Worker`](https://nodejs.org/api/worker_threads.html#class-worker)
6+
- `forks` pool runs every test file in a separate [forked child process](https://nodejs.org/api/child_process.html#child_processforkmodulepath-args-options)
7+
- `vmThreads` pool runs every test file in a separate [VM context](https://nodejs.org/api/vm.html#vmcreatecontextcontextobject-options), but it uses workers for parallelism
8+
9+
This greatly increases test times, which might not be desirable for projects that don't rely on side effects and properly cleanup their state (which is usually true for projects with `node` environment). In this case disabling isolation will improve the speed of your tests. To do that, you can provide `--no-isolate` flag to the CLI or set [`test.isolate`](/config/#isolate) property in the config to `false`. If you are using several pools at once with `poolMatchGlobs`, you can also disable isolation for a specific pool you are using.
10+
11+
::: code-group
12+
```bash [CLI]
13+
vitest --no-isolate
14+
```
15+
```ts [vitest.config.js]
16+
import { defineConfig } from 'vitest/config'
17+
18+
export default defineConfig({
19+
test: {
20+
isolate: false,
21+
// you can also disable isolation only for specific pools
22+
poolOptions: {
23+
forks: {
24+
isolate: false,
25+
},
26+
},
27+
},
28+
})
29+
```
30+
:::
31+
32+
::: note
33+
If you are using `vmThreads` pool, you cannot disable isolation. Use `threads` pool instead to improve your tests performance.
34+
:::
35+
36+
For some projects, it might also be desirable to disable parallelism to improve startup time. To do that, provide `--no-file-parallelism` flag to the CLI or set [`test.fileParallelism`](/config/#fileParallelism) property in the config to `false`.
37+
38+
::: code-group
39+
```bash [CLI]
40+
vitest --no-file-parallelism
41+
```
42+
```ts [vitest.config.js]
43+
import { defineConfig } from 'vitest/config'
44+
45+
export default defineConfig({
46+
test: {
47+
fileParallelism: false,
48+
},
49+
})
50+
```
51+
:::

‎packages/vitest/src/defaults.ts

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export const fakeTimersDefaults = {
6262

6363
const config = {
6464
allowOnly: !isCI,
65+
isolate: true,
6566
watch: !isCI,
6667
globals: false,
6768
environment: 'node' as const,

‎packages/vitest/src/node/cli.ts

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ cli
3030
.option('--run', 'Disable watch mode')
3131
.option('--mode <name>', 'Override Vite mode (default: test)')
3232
.option('--workspace <path>', 'Path to a workspace configuration file')
33+
.option('--isolate', 'Run every test file in isolation. To disable isolation, use --no-isolate (default: true)')
3334
.option('--globals', 'Inject apis globally')
3435
.option('--dom', 'Mock browser API with happy-dom')
3536
.option('--browser [options]', 'Run tests in the browser (default: false)')

‎packages/vitest/src/node/core.ts

+1
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ export class Vitest {
281281
'testNamePattern',
282282
'passWithNoTests',
283283
'bail',
284+
'isolate',
284285
] as const
285286

286287
const cliOverrides = overridesOptions.reduce((acc, name) => {

‎packages/vitest/src/node/plugins/index.ts

+10
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('t
5151
)
5252
testConfig.api = resolveApiServerConfig(testConfig)
5353

54+
testConfig.poolOptions ??= {}
55+
testConfig.poolOptions.threads ??= {}
56+
testConfig.poolOptions.forks ??= {}
57+
// prefer --poolOptions.{name}.isolate CLI arguments over --isolate, but still respect config value
58+
testConfig.poolOptions.threads.isolate = options.poolOptions?.threads?.isolate ?? options.isolate ?? testConfig.poolOptions.threads.isolate ?? viteConfig.test?.isolate
59+
testConfig.poolOptions.forks.isolate = options.poolOptions?.forks?.isolate ?? options.isolate ?? testConfig.poolOptions.forks.isolate ?? viteConfig.test?.isolate
60+
5461
// store defines for globalThis to make them
5562
// reassignable when running in worker in src/runtime/setup.ts
5663
const defines: Record<string, any> = deleteDefineConfig(viteConfig)
@@ -91,6 +98,9 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest('t
9198
allow: resolveFsAllow(getRoot(), testConfig.config),
9299
},
93100
},
101+
test: {
102+
poolOptions: testConfig.poolOptions,
103+
},
94104
}
95105

96106
// chokidar fsevents is unstable on macos when emitting "ready" event

‎packages/vitest/src/types/config.ts

+9
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,15 @@ export interface InlineConfig {
280280
*/
281281
environmentMatchGlobs?: [string, VitestEnvironment][]
282282

283+
/**
284+
* Run tests in an isolated environment. This option has no effect on vmThreads pool.
285+
*
286+
* Disabling this option might improve performance if your code doesn't rely on side effects.
287+
*
288+
* @default true
289+
*/
290+
isolate?: boolean
291+
283292
/**
284293
* Pool used to run tests in.
285294
*

‎test/config/test/resolution.test.ts

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import type { UserConfig } from 'vitest'
2+
import { describe, expect, it } from 'vitest'
3+
import { createVitest } from 'vitest/node'
4+
5+
async function config(cliOptions: UserConfig, configValue: UserConfig = {}) {
6+
const vitest = await createVitest('test', { ...cliOptions, watch: false }, { test: configValue })
7+
return vitest.config
8+
}
9+
10+
describe('correctly defines isolated flags', async () => {
11+
it('prefers CLI poolOptions flags over config', async () => {
12+
const c = await config({
13+
isolate: true,
14+
poolOptions: {
15+
threads: {
16+
isolate: false,
17+
},
18+
forks: {
19+
isolate: false,
20+
},
21+
},
22+
})
23+
expect(c.poolOptions?.threads?.isolate).toBe(false)
24+
expect(c.poolOptions?.forks?.isolate).toBe(false)
25+
expect(c.isolate).toBe(true)
26+
})
27+
28+
it('override CLI poolOptions flags over isolate', async () => {
29+
const c = await config({
30+
isolate: false,
31+
poolOptions: {
32+
threads: {
33+
isolate: true,
34+
},
35+
forks: {
36+
isolate: true,
37+
},
38+
},
39+
}, {
40+
poolOptions: {
41+
threads: {
42+
isolate: false,
43+
},
44+
forks: {
45+
isolate: false,
46+
},
47+
},
48+
})
49+
expect(c.poolOptions?.threads?.isolate).toBe(true)
50+
expect(c.poolOptions?.forks?.isolate).toBe(true)
51+
expect(c.isolate).toBe(false)
52+
})
53+
54+
it('override CLI isolate flag if poolOptions is not set via CLI', async () => {
55+
const c = await config({
56+
isolate: true,
57+
}, {
58+
poolOptions: {
59+
threads: {
60+
isolate: false,
61+
},
62+
forks: {
63+
isolate: false,
64+
},
65+
},
66+
})
67+
expect(c.poolOptions?.threads?.isolate).toBe(true)
68+
expect(c.poolOptions?.forks?.isolate).toBe(true)
69+
expect(c.isolate).toBe(true)
70+
})
71+
72+
it('keeps user configured poolOptions if no CLI flag is provided', async () => {
73+
const c = await config({}, {
74+
poolOptions: {
75+
threads: {
76+
isolate: false,
77+
},
78+
forks: {
79+
isolate: false,
80+
},
81+
},
82+
})
83+
expect(c.poolOptions?.threads?.isolate).toBe(false)
84+
expect(c.poolOptions?.forks?.isolate).toBe(false)
85+
expect(c.isolate).toBe(true)
86+
})
87+
88+
it('isolate config value overrides poolOptions defaults', async () => {
89+
const c = await config({}, {
90+
isolate: false,
91+
})
92+
expect(c.poolOptions?.threads?.isolate).toBe(false)
93+
expect(c.poolOptions?.forks?.isolate).toBe(false)
94+
expect(c.isolate).toBe(false)
95+
})
96+
97+
it('if no isolation is defined in the config, fallback ot undefined', async () => {
98+
const c = await config({}, {})
99+
expect(c.poolOptions?.threads?.isolate).toBe(undefined)
100+
expect(c.poolOptions?.forks?.isolate).toBe(undefined)
101+
// set in configDefaults, so it's always defined
102+
expect(c.isolate).toBe(true)
103+
})
104+
})

‎test/run-once/vitest.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ import { defineConfig } from 'vite'
22

33
export default defineConfig({
44
test: {
5-
poolOptions: { threads: { isolate: false } },
5+
isolate: false,
66
},
77
})

‎test/stacktraces/fixtures/vite.config.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,8 @@ export default defineConfig({
4141
},
4242
}],
4343
test: {
44+
isolate: false,
4445
pool: 'forks',
45-
poolOptions: {
46-
forks: {
47-
isolate: false,
48-
},
49-
},
5046
include: ['**/*.{test,spec}.{imba,?(c|m)[jt]s?(x)}'],
5147
setupFiles: ['./setup.js'],
5248
},

0 commit comments

Comments
 (0)
Please sign in to comment.