Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
Vitest
A blazing fast unit test framework powered by Vite.
💖 This project is currently in closed beta exclusively for Sponsors.
Become a Sponsor of @patak-dev or @antfu to access the source code and issues tracker.
⚠️ DISCLAIMER: Vitest is still in development and not stable yet. It's not recommended to use it in production.
Vitest requires Vite v2.7 and Node v16
Features
- Vite's config, transformers, resolvers, and plugins. Use the same setup from your app!
- Jest Snapshot
- Chai built-in for assertions, with Jest expect compatible APIs.
- Smart & instant watch mode, like HMR for tests!
- Native code coverage via c8
- Sinon built-in for mocking, stubbing, and spies.
- JSDOM and happy-dom for DOM and browser API mocking
- Components testing (Vue, React, Lit, Vitesse)
- Workers multi-threading via Piscina
- ESM first, top level await
- Out-of-box TypeScript / JSX support
- Filtering, timeouts, concurrent for suite and tests
import { it, describe, expect, assert } from 'vitest'
describe('suite name', () => {
it('foo', () => {
expect(1 + 1).toEqual(2)
expect(true).to.be.true
})
it('bar', () => {
assert.equal(Math.sqrt(4), 2)
})
it('snapshot', () => {
expect({ foo: 'bar' }).toMatchSnapshot()
})
})
$ npx vitest
Examples
- Unit Testing
- Vue Component Testing
- React Component Testing
- Lit Component Testing
- Vitesse Component Testing
Projects using Vitest
Configuration
vitest will read your root vite.config.ts when it present to match with the plugins and setup as your Vite app. If you want to it to have a different configuration for testing, you could either:
- Create
vitest.config.ts, which will have the higher priority - Pass
--configoption to CLI, e.g.vitest --config ./path/to/vitest.config.ts - Use
process.env.VITESTto conditionally apply different configuration invite.config.ts
To configure vitest itself, add test property in your Vite config
// vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
test: {
// ...
}
})
Global APIs
By default, vitest does not provide global APIs for explicitness. If you prefer to use the APIs globally like Jest, you can pass the --global option to CLI or add global: true in the config.
// vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
test: {
global: true
}
})
To get TypeScript working with the global APIs, add vitest/global to the types filed in your tsconfig.json
// tsconfig.json
{
"compilerOptions": {
"types": [
"vitest/global"
]
}
}
If you are already using unplugin-auto-import in your project, you can also use it directly for auto importing those APIs.
// vite.config.ts
import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig({
plugins: [
AutoImport({
imports: ['vitest'],
dts: true // generate TypeScript declaration
})
]
})
Browser Mocking
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 'vite'
export default defineConfig({
test: {
environment: 'happy-dom' // or 'jsdom', 'node'
}
})
Watch Mode
$ vitest -w
Vitest smartly searches the module graph and only rerun the related tests (just like how HMR works in Vite!).
Coverage
Vitest works perfectly with c8
$ npm i -D c8
$ c8 vitest
{
"scripts": {
"test": "vitest",
"coverage": "c8 vitest"
}
}
For convenience, we also provide a shorthand for passing --coverage option to CLI, which will wrap the process with c8 for you. Note when using the shorthand, you will lose the ability to pass additional options to c8.
$ vitest --coverage
For more configuration available, please refer to c8's documentation.
Filtering
CLI
You can use CLI to filter test files my name:
$ vitest basic
Will only execute test files that contain basic, e.g.
basic.test.ts
basic-foo.test.ts
Specifying a Timeout
You can optionally pass a timeout in milliseconds as third argument to tests. The default is 5 seconds.
test('name', async() => { ... }, 1000)
Hooks also can receive a timeout, with the same 5 seconds default.
beforeAll( async() => { ... }, 1000)
Skipping suites and tests
Use .skip to avoid running certain suites or tests
describe.skip('skipped suite', () => {
it('test', () => {
// Suite skipped, no error
assert.equal(Math.sqrt(4), 3)
})
})
describe('suite', () => {
it.skip('skipped test', () => {
// Test skipped, no error
assert.equal(Math.sqrt(4), 3)
})
})
Selecting suites and tests to run
Use .only to only run certain suites or tests
// Only this suite (and others marked with only) are run
describe.only('suite', () => {
it('test', () => {
assert.equal(Math.sqrt(4), 3)
})
})
describe('another suite', () => {
it('skipped test', () => {
// Test skipped, as tests are running in Only mode
assert.equal(Math.sqrt(4), 3)
})
it.only('test', () => {
// Only this test (and others marked with only) are run
assert.equal(Math.sqrt(4), 2)
})
})
Unimplemented suites and tests
Use .todo to stub suites and tests that should be implemented
// An entry will be shown in the report for this suite
describe.todo('unimplemented suite')
// An entry will be shown in the report for this test
describe('suite', () => {
it.todo('unimplemented test')
})
Running tests concurrently
Use .concurrent in consecutive tests to run them in parallel
// The two tests marked with concurrent will be run in parallel
describe('suite', () => {
it('serial test', () => {
assert.equal(Math.sqrt(4), 3)
})
it.concurrent('concurrent test 1', () => {
assert.equal(Math.sqrt(4), 3)
})
it.concurrent('concurrent test 2', () => {
assert.equal(Math.sqrt(4), 3)
})
})
If you use .concurrent in a suite, every tests in it will be run in parallel
// The two tests marked with concurrent will be run in parallel
describe.concurrent('suite', () => {
it('concurrent test 1', () => {
assert.equal(Math.sqrt(4), 3)
})
it('concurrent test 2', () => {
assert.equal(Math.sqrt(4), 3)
})
// No effect, same as not using .concurrent
it.concurrent('concurrent test 3', () => {
assert.equal(Math.sqrt(4), 3)
})
})
You can also use .skip, .only, and .todo with concurrent suite and tests. All the following combinations are valid:
describe.concurrent(...)
describe.skip.concurrent(...)
describe.concurrent.skip(...)
describe.only.concurrent(...)
describe.concurrent.only(...)
describe.todo.concurrent(...)
describe.concurrent.todo(...)
it.concurrent(...)
it.skip.concurrent(...)
it.concurrent.skip(...)
it.only.concurrent(...)
it.concurrent.only(...)
it.todo.concurrent(...)
it.concurrent.todo(...)
Sponsors
Credits
Thanks to:
- @patak-dev for the awesome package name!
- The Vite team for brainstorming the initial idea.
- @pi0 for the idea and implementation of using Vite to transform and bundle the server code.
- @lukeed for the work on uvu where we are inspired a lot from.
License
MIT License © 2021 Anthony Fu