本文最后更新于:2024年3月25日 凌晨
简介
vitest 是一个新的单元测试工具,它很快,默认支持 esm,兼容 jest api,可以被视为更好的 jest。在默认情况下,它支持以下功能
- 很快
- 支持 esm
- 支持 ts
- 兼容 jest api
- 支持 vite 的功能
- 支持多框架 react/vue
官网: https://vitest.dev/
安装
vitest 内部依赖 vite,但不需要安装 vite。
配置
好吧,实际上 vitest 是真正的零配置,支持 ts/esm/tsx,但如果你想要更多的功能,确实可以创建 vitest.config.ts 文件或者直接在 vite.config.ts 中添加配置。
基本示例
基本上,它与 jest 类似,在要测试文件的同级目录创建一个 __tests__
文件夹,然后在其中创建一个文件,文件名以 .test.ts 结尾,文件内容如下
1 2 3 4 5
| import { expect, it } from 'vitest'
it('hello', () => { expect(1 + 2).eq(3) })
|
然后运行 pnpm vitest hello.test.ts
就可以以监视模式运行单元测试了
如果有多个测试时可以使用 describe
分组
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') }) })
|
也可以使用 -t 参数指定要执行的测试,例如
1
| pnpm vitest hello.test.ts -t 'add'
|
也可以使用 it.only/descrive.only
来指定要执行的测试
另外一些 beforeEach/afterEach 等钩子函数也是支持的
例如默认创建一个测试目录
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 }) })
|
断言
vitest 默认内嵌 chaijs 作为断言库,并且兼容了 jest 的断言 api。
最有趣的是,它支持某些断言很简洁,例如断言一些常见的值
1 2 3 4
| expect(true).true expect(false).false expect(undefined).undefined expect(null).null
|
或者对比值
1 2
| expect(1).eq(1) expect({ name: 'world' }).deep.eq({ name: 'world' })
|
断言数组中包含指定值
1
| expect([1, 2, 3]).members([1, 2, 3])
|
web api polyfill
通常在 web 项目中测试时也会包含一些 dom api,例如 localStorage/indexedDB 等。
happy-dom
dom 相关的 api mock 可以通过 happy-dom
来实现,它是一个简单的 dom 实现,可以在 node 中运行,虽然 api 比 jsdom 少一些,但速度要更快。
配置
1 2 3 4 5
| { "test": { "environment": "happy-dom" } }
|
使用 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 支持了 fetch 请求,不再需要 polyfill。
indexedDB
遗憾的是 hyppy-dom 没有实现 indexedDB,所以我们需要使用 fake-indexeddb
做 polyfill。
这个不需要做什么配置,只需要在测试文件中引入即可
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) })
|
局限性
- vitest 有一个 vscode 插件,但目前尚未支持 monorepo,所以建议不要使用
- vitest 基于 vite,
__dirname/__filename
等变量在测试中可用,但实际上在 nodejs esm 中不可用
- vitest 与 node:test 的 api 有重叠,尤其是 it/describe,要留意不要错误引用