Custom Pool
WARNING
This is advanced API. If you just want to run tests, you probably don't need this. It is primarily used by library authors.
Vitest runs tests in pools. By default, there are several pools:
threads
to run tests usingnode:worker_threads
(isolation is provided with a new worker context)forks
to run tests usingnode:child_process
(isolation is provided with a newchild_process.fork
process)vmThreads
to run tests usingnode:worker_threads
(but isolation is provided withvm
module instead of a new worker context)browser
to run tests using browser providerstypescript
to run typechecking on tests
You can provide your own pool by specifying a file path:
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
// will run every file with a custom pool by default
pool: './my-custom-pool.ts',
// you can provide options using `poolOptions` object
poolOptions: {
myCustomPool: {
customProperty: true,
},
},
// you can also specify pool for a subset of files
poolMatchGlobs: [
['**/*.custom.test.ts', './my-custom-pool.ts'],
],
},
})
API
The file specified in pool
option should export a function (can be async) that accepts Vitest
interface as its first option. This function needs to return an object matching ProcessPool
interface:
import { ProcessPool, TestSpecification } from 'vitest/node'
export interface ProcessPool {
name: string
runTests: (files: TestSpecification[], invalidates?: string[]) => Promise<void>
collectTests: (files: TestSpecification[], invalidates?: string[]) => Promise<void>
close?: () => Promise<void>
}
The function is called only once (unless the server config was updated), and it's generally a good idea to initialize everything you need for tests inside that function and reuse it when runTests
is called.
Vitest calls runTest
when new tests are scheduled to run. It will not call it if files
is empty. The first argument is an array of tuples: the first element is a reference to a workspace project and the second one is an absolute path to a test file. Files are sorted using sequencer
before runTests
is called. It's possible (but unlikely) to have the same file twice, but it will always have a different project - this is implemented via vitest.workspace.ts
configuration.
Vitest will wait until runTests
is executed before finishing a run (i.e., it will emit onFinished
only after runTests
is resolved).
If you are using a custom pool, you will have to provide test files and their results yourself - you can reference vitest.state
for that (most important are collectFiles
and updateTasks
). Vitest uses startTests
function from @vitest/runner
package to do that.
Vitest will call collectTests
if vitest.collect
is called or vitest list
is invoked via a CLI command. It works the same way as runTests
, but you don't have to run test callbacks, only report their tasks by calling vitest.state.collectFiles(files)
.
To communicate between different processes, you can create methods object using createMethodsRPC
from vitest/node
, and use any form of communication that you prefer. For example, to use WebSockets with birpc
you can write something like this:
import { createBirpc } from 'birpc'
import { parse, stringify } from 'flatted'
import { createMethodsRPC, WorkspaceProject } from 'vitest/node'
function createRpc(project: WorkspaceProject, wss: WebSocketServer) {
return createBirpc(
createMethodsRPC(project),
{
post: msg => wss.send(msg),
on: fn => wss.on('message', fn),
serialize: stringify,
deserialize: parse,
},
)
}
To make sure every test is collected, you would call ctx.state.collectFiles
and report it to Vitest reporters:
async function runTests(project: WorkspaceProject, tests: string[]) {
// ... running tests, put into "files" and "tasks"
const methods = createMethodsRPC(project)
await methods.onCollected(files)
// most reporters rely on results being updated in "onTaskUpdate"
await methods.onTaskUpdate(tasks)
}
You can see a simple example in pool/custom-pool.ts.