Introduction

vitest is a new unit testing tool that is fast, supports esm by default, is compatible with the jest api, and can be considered a better jest. by default, it supports the following features being used

  • very fast
  • esm support
  • ts support
  • Compatible with jest api
  • support for vite
  • support for multiple frameworks react/vue

Installation

vitest internally depends on vite, but it is not necessary to install vite.

1
npm i -D vitest

Configuration

Well, actually vitest is really zero configuration and supports ts/esm/tsx, but if you want more features, you can indeed create vitest.config.ts file or add configuration directly to vite.config.ts.

Basic example

Basically, it is similar to jest in that you create a __tests__ folder in the same directory as the file you want to test, and then create a file in it with a file name ending in .test.ts and the following contents.

1
2
3
4
5
import { expect, it } from 'vitest'

it('hello', () => {
  expect(1 + 2).eq(3)
})

Then run pnpm vitest hello.test.ts to run the unit tests in monitor mode.

You can use describe groups if you have multiple tests.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import { describe, expect, it } from 'vitest'

describe('math', () => {
  it('add', () => {
    expect(1 + 2).eq(3)
  })
  it('less', () => {
    expect(1 - 2).eq(-1)
  })
})

describe('string', () => {
  it('hello', () => {
    const hello = (name: string) => `hello ${name}`
    expect(hello('world')).eq('hello world')
  })
})

The -t parameter can also be used to specify the test to be executed, for example

1
pnpm vitest hello.test.ts -t 'add'

It is also possible to use it.only/descrive.only to specify the tests to be executed.

Also some hook functions like beforeEach/afterEach are supported.

For example, a test directory is created by default.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const tempPath = path.resolve(__dirname, '.temp', path.basename(__filename))
beforeEach(async () => {
  await rm(tempPath, { recursive: true, force: true })
  await mkdir(tempPath, { recursive: true })
})
afterEach(async () => {
  await rm(tempPath, { recursive: true, force: true })
})

Assertions

vitest has chaijs built in by default as an assertion library and is compatible with jest’s assertion api.

Most interestingly, it supports some assertions very concisely, such as asserting some common values.

1
2
3
4
expect(true).true
expect(false).false
expect(undefined).undefined
expect(null).null

Or compare the values.

1
2
expect(1).eq(1)
expect({ name: 'world' }).deep.eq({ name: 'world' }) // Depth Comparison

web api polyfill

Usually some dom api are included in web projects for testing, such as localStorage/indexedDB etc.

happy-dom

The dom-related api mock can be implemented with happy-dom, which is a simple dom implementation that runs in node and has less api than jsdom, but is faster.

Configuration.

1
2
3
4
5
{
  "test": {
    "environment": "happy-dom"
  }
}

Use localStorage.

1
2
3
4
expect(localStorage.getItem('test')).null
localStorage.setItem('test', 'test')
expect(localStorage.getItem('test')).eq('test')
localStorage.removeItem('test')

fetch

nodejs@18 supports fetch requests and no longer requires polyfill.

indexedDB

Unfortunately hyppy-dom doesn’t implement indexedDB, so we need to use fake-indexeddb to do the polyfill. This doesn’t require much configuration, just import it in the test file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import 'fake-indexeddb/auto'
import { openDB } from 'idb'

it('indexeddb', async () => {
  const db = await openDB<{
    books: {
      title: string
      author: string
      isbn: string
    }
  }>('test', 1, {
    upgrade(db) {
      db.createObjectStore('books', {
        autoIncrement: true,
        keyPath: 'isbn',
      })
    },
  })
  expect(await db.getAll('books')).empty
  await db.add('books', {
    title: 'Quarry Memories',
    author: 'Fred',
    isbn: 123456,
  })
  await db.add('books', {
    title: 'Water Buffaloes',
    author: 'Fred',
    isbn: 234567,
  })
  expect(await db.getAll('books')).length(2)
})

Limitations

  • vitest has a vscode plugin, but monorepo is not yet supported, so it is recommended not to use it
  • vitest is based on vite, and variables like __dirname/__filename are available in tests, but not actually available in nodejs esm
  • vitest has overlap with the node:test api, especially it/describe, so be careful not to misquote it