Features
- Vite's config, transformers, resolvers, and plugins.
- Use the same setup from your app to run the tests!
- Components testing for Vue, React, Svelte, Lit and more
- Out-of-box TypeScript / JSX support
- ESM first, top level await
- Workers multi-threading via tinypool
- Filtering, timeouts, concurrent for suite and tests
- Chai built-in for assertions + Jest expect compatible APIs
- Tinyspy built-in for mocking
- Native code coverage via c8
- Rust like in-source testing
Shared config between test, dev and build
Vite's config, transformers, resolvers, and plugins. Use the same setup from your app to run the tests.
Learn more at Configuring Vitest
Watch Mode
$ vitest
When you modify your source code or the test files, Vitest smartly searches the module graph and only rerun the related tests, just like how HMR works in Vite!
vitest
starts in watch mode
by default in development environment and run mode
in CI environment (when process.
presents) smartly. You can use vitest watch
or vitest run
to explicitly specify the desired mode.
Common web idioms out-of-the-box
Out-of-box ES Module / TypeScript / JSX support / PostCSS
Threads
Workers multi-threading via tinypool (a lightweight fork of Piscina), allowing tests to run simultaneously. Threads are enabled by default in Vitest, and can be disabled passing --no-threads
in the CLI.
Vitest also isolates each file's environment so env mutations in one file don't affect others. Isolation can be disabled by passing --no-isolate
to the CLI (trading of correctness for run performance).
Test Filtering
Vitest provided many ways to narrow down the tests to run to speed up and focus during the development.
Learn more about Test Filtering
Running tests concurrently
Use .concurrent
in consecutive tests to run them in parallel
import { describe, it } from 'vitest'
// The two tests marked with concurrent will be run in parallel
describe('suite', () => {
it('serial test', async () => { /* ... */ })
it.concurrent('concurrent test 1', async () => { /* ... */ })
it.concurrent('concurrent test 2', async () => { /* ... */ })
})
If you use .concurrent
in a suite, every tests in it will be run in parallel
import { describe, it } from 'vitest'
// All tests within this suite will be run in parallel
describe.concurrent('suite', () => {
it('concurrent test 1', async () => { /* ... */ })
it('concurrent test 2', async () => { /* ... */ })
it.concurrent('concurrent test 3', async () => { /* ... */ })
})
You can also use .skip
, .only
, and .todo
with concurrent suites and tests. Read more in the API Reference
Snapshot
Jest Snapshot support
Chai and Jest expect compatibility
Chai built-in for assertions plus Jest expect compatible APIs
Notice that if you are using third-party libraries that add matchers, setting test.globals
to true
will provide better compatibility
Mocking
Tinyspy built-in for mocking with jest
compatible APIs on vi
object.
import { expect, vi } from 'vitest'
const fn = vi.fn()
fn('hello', 1)
expect(vi.isMockFunction(fn)).toBe(true)
expect(fn.mock.calls[0]).toEqual(['hello', 1])
fn.mockImplementation(arg => arg)
fn('world', 2)
expect(fn.mock.results[1]).toBe('world')
Vitest supports both happy-dom or jsdom for mocking DOM and browser APIs. They don't come with Vitest, you might need to install them:
$ npm i -D happy-dom
# or
$ npm i -D jsdom
After that, change the environment
option in your config file:
// vite.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
environment: 'happy-dom', // or 'jsdom', 'node'
},
})
Learn more at Mocking
Coverage
Vitest supports Native code coverage via c8
{
"scripts": {
"test": "vitest",
"coverage": "vitest run --coverage"
}
}
To configure it, set test.coverage
options in your config file:
// vite.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
coverage: {
reporter: ['text', 'json', 'html'],
},
},
})
In-source testing
Vitest also provides a way to run tests with in your source code along with the implementation, simliar to Rust's module tests.
This makes the tests share the same closure as the implementations and able to test against private states without exporting. Meanwhile, it also brings the closer feedback loop for development.
// src/index.ts
// the implementation
export function add(...args: number[]) {
return args.reduce((a, b) => a + b, 0)
}
// in-source test suites
if (import.meta.vitest) {
const { it, expect } = import.meta.vitest
it('add', () => {
expect(add()).toBe(0)
expect(add(1)).toBe(1)
expect(add(1, 2, 3)).toBe(6)
})
}
Learn more at In-source testing