前言#
来都来了,那就把Vitest也学了~
官方文档#
简介#

是一个由vite提供的快速的单元测试框架
没了~简洁明了
另外,vitest需要vite版本3.0以上以及node版本14以上。

目前bug还是挺多的(小声bibi~)
上手#
其实官方有直接提供线上调试工具,如果你不想一步一步的配置的话,你可以直接通过线上链接选择自己想学习的配置,点击play online一步到位。
Vitestvitest.dev/guide/#examples
不过学习嘛,还是得一步一步的来
老规矩先创建环境
mkdir vitest-study
cd vitest-study
npm init --yes然后直接安装vitest以及vite[1]
npm install vite vitest -D 然后创建下配置文件
这里一定要配置的文件是vite.config.js,不然你的项目跑不起来。而vitest的配置信息是可以配置到vite的配置文件中的。
让我们来创建vite.config.js文件, 你也可以直接npm create vite@lastest直接安装vite app。
这里就没必要了,因为现在还不需要引入框架。
import { defineConfig } from 'vite'
export default defineConfig({
test: {}
})然后新增两个文件__tests__/index.spec.js 以及src/index.js。
其中index.js随便写个方法导出去
export function testName (name) {
return name
} index.spec.js中导入并导入vitest的方法
import { expect, test } from 'vitest'
import { testName } from '../src/index'
test('vitest-test', () => {
expect(testName('jack')).toBe('jack')
})
package.json`中新增`script
"test": "vitest"现在就能直接跑啦~终端输入npm run test

配置起来不要太舒服~简直太傻瓜了
不过我在跑的时候遇到了个奇葩的问题,一直报错乱码,我一看,vscode居然给我编码为utf-16,我也是醉了,然后改回utf-8就正常了。
然后我们将配置改到vitest.config.js文件中。顺便来试下client端测试,这需要client环境模拟的包jsdom[2]
npm install jsdom -D然后在vitest.config.js中配置下
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
environment: 'jsdom'
}
}) 这里有一个点需要注意,这里导入的defineConfig是来自vitest包里的,别给导错了。
别忘了先移除vite.config.js中的配置,当然,你也可以合并。
import { mergeConfig } from 'vite'
import { defineConfig } from 'vitest/config'
import viteConfig from './vite.config'
export default mergeConfig(viteConfig, defineConfig({
test: {
//...
},
}))新建src/client.js和__tests__/client.spec.js俩文件
然后在client.js中导出下dom
export const domStr = `
<div id="test" class="test">123</div>
` 在client.spec.js中导入,并写入document中
/**
* @vitest-environment jsdom
*/
import { expect, test } from 'vitest'
import { domStr } from '../src/client'
test('test-client', () => {
document.body.innerHTML = domStr
expect(document.getElementById('test').innerHTML).contain('666')
})然后终端直接运行npm run test

正常运行。
CLI以及指令#
刚测试的时候都是直接vitest一把梭,把所有的文件都算进去了。有的时候我们只想测试其中一个文件或者一个小测试元,那么就需要改下我们的指令。
- 只测试某个文件
"scripts": {
"test": "vitest __tests__/client.spec.js"
},- 只测试某个测试元
"scripts": {
"test": "vitest -t test-client"
},但是这里还是得配置在package.json中然后用npm run才能跑或者得基于npx来跑。 很麻烦,尤其是每次要改变测试目标的时候就得改来改去,有没有办法直接在终端改呢?
当然有,官方也提供了cli来方便我们终端直接输入。[4]
全局安装vitest以及vite,没了vite(而且是3.x),这个vitest就没法跑
npm install vite vitest -g然而装了我也没法跑。。。。。
我遇到了不知名问题,可能是bug,找得到文件夹,找得到文件,就是找不到测试元。。。

如果你跑成功了,麻烦评论里和我说下啥问题。。。。
补:我终于跑成功了,当我用in-source test也就是在代码里面写测试代码import.meta.vitest(目录里的第八点)这种写法就可以正常被侦测到

但是这样每次在终端输入还是有些麻烦了,我们还有没有其他方式呢?
当然有,往下看。
vscode拓展推荐#

Vitest会检测你的测试文件,发现后会在你的测试元左侧出现一个小按钮,点击就能直接运行

和jest拓展一样,Vitest Runner都是左上角多出一个小run | debug的按钮,所以就不演示了。不过你一定要确定这个工作区中没有安装jest的拓展(工作区禁用),不然会有冲突。
可视化界面#
一直在终端看会不会看的脖子疼?
vitest提供了ui界面,专治终端界面小且字小难看的问题。(专治颜值癌~)
我们先安装对应的包@vitest/ui
npm install @vitest/ui 然后package.json文件中新增个script
"ui": "vitest --ui"我们直接在终端跑下
npm run ui
是不是很好看doge?如果嫌亮还能切夜晚模式,真得劲。
在你的代码中写测试代码#
你是否有需求在你的代码中写测试代码而不是每次都要导出测试?
官方也提供了这种方式。
我们先改动vitest.config.js中的配置,支持in-source
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
environment: 'node',
includeSource: ['src/*.{js,ts}']
}
}) 接着直接新开一个文件src/in-source.js
function sum (a, b) {
return a + b
}
if (import.meta.vitest) {
const { it, expect } = import.meta.vitest
it('test-in-source', () => {
expect(sum(1, 3)).toBe(4)
})
}然后直接npm run ui

运行成功!
上下文环境#
真实开发过程中是很难做到测试内容都在同一个模块中,基本是多个测试元共同测试,而这些测试元之间又需要数据共享,但是这些测试元的上下文环境又是独立的,这时就需要有一个地方存储共同的数据以及将数据传入测试元的地方。又或者你有一大段相同逻辑,你就可以都放到beforeEach或者afterEach中。
我们先来看下测试元自己的context,是通过回调参数传入的

存放的是一些测试元自己的信息。
他们提供了两个hook和jest一样:beforeEach和afterEach会在测试元执行前、执行后执行。
我们放个cat对象,然后在这个beforeEach中加上cat对象,然后新建cat.js文件新增setName和setAge的方法
import { beforeEach } from "vitest"
const cat = {}
beforeEach((ctx) => {
ctx.cat = cat
})
function setName (target, name) {
target.name = name
return target
}
if (import.meta.vitest) {
const { it, expect } = import.meta.vitest
it('set-name', (ctx) => {
expect(setName(ctx.cat, 'tom').cat.name).toBe('tom')
})
}
这样就拿到了。
这里为什么不直接在test中直接ctx.cat = cat呢?试想一下如果这是一段上百行的代码你就会发现很有用了,将相同的代码合并到一起,减少每个测试元自身的逻辑。
client端测试#
这个【上手】中有说到了,这里就不多说了。
自定义matcher#
官方提供的matcher实际上已经够很多场景用了,但是如果你还是觉得不满意,官方也提供了拓展入口。
我们新增一个文件src/is-jack.js
if (import.meta.vitest) {
const { expect, it } = import.meta.vitest
expect.extend({
toBeJack (rec, exp) {
return {
pass: rec === 'jack',
message: () => `expect jack but get ${rec}`
}
}
})
it('test-extend', () => {
expect('dan').toBeJack()
})
} 
这个也正常,不多说。
调试#
没想到吧,测试元也需要调试,鬼知道测试元里的代码对不对,有代码的地方那就会有bug,这是“真理”doge。
官方提供了两种方式,一种基于IDE另一种自然是不依赖于IDE 。
我们先来看下不依赖IDE的。
我们安装下一个包ndb[10] 。这个包依赖于一个谷歌提供的包puppeteer[11] 。这是一个无头浏览器,基于chrome的内核,感兴趣的可自行查阅。后面我也会写个文章。 (拉的时候记得那啥,不然包可能拉不下来。。。又或者你去找别人的独立包)。另外注意这个包需要全局安装才行。
安装完之后直接ndb npm run test
然后会弹出一个chrome dev-tools,然后就是熟悉的debug了,你也可以在你的代码中写debugger来调试。

别忘了用正确的方式关闭这个进程,不然等着后台疯狂偷吃资源吧。。。要不是笔记本的风扇开始告诉转起来我还不知道呢。。。
然后我们再来看下依赖IDE的方式。这个咱们熟~。
而我个人只熟vscode,idea真不熟,doge。
老规矩直接创建项目内的launch.json,然后将下面的代码复制到文件中。
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Current Test File",
"autoAttachChildProcesses": true,
"skipFiles": ["<node_internals>/**", "**/node_modules/**"],
"program": "${workspaceRoot}/node_modules/vitest/vitest.mjs",
"args": ["run", "${relativeFile}"],
"smartStep": true,
"console": "integratedTerminal"
}
]
}
配置信息还是那几样,如果不清楚可以去看下我之前的文章。
然后直接F5

就是这么简单~
api#
就说几个我个人觉得比较有用的,其他自行看文档即可。
test别名it,老朋友了,不多说。
import { expect, test } from 'vitest'
test('should work as expected', () => {
expect(Math.sqrt(4)).toBe(2)
})test.skipIf: 带条件过滤,根据条件判断是否执行。
import { assert, test } from 'vitest'
const isDev = process.env.NODE_ENV === 'development'
test.skipIf(isDev)('prod only test', () => {
// this test only runs in production
}) -
test.runif: 不多说,用法如上 -
test.only: 只测试这个测试元儿~ -
test.concurrent[13]并行
test.concurrent(/* ... */)
test.skip.concurrent('concurrent test 2', async ({ expect }) => { /* ... */ }) // or test.concurrent.skip(/* ... */)
test.only.concurrent('concurrent test 3', async ({ expect }) => { /* ... */ }) // or test.concurrent.only(/* ... */)
test.todo.concurrent('concurrent test 1', async ({ expect }) => { /* ... */ }) // or test.concurrent.todo(/* ... */)另外需要注意的是,这里的expect需要从ctx里拿才行,这样才能确保你的表达式被侦测到。
test.todo: 就像你在写代码的时候未完成的地方会写个注释TODO一样,如果这个测试元并没有写完,可以加个todo,到时候跑的时候会被收集并且展示给你看目前还有多少个测试元未完成。

test.each[14]: 如果你有多组数据需要测试,那么你就得跑N次测试元,官方这里提供了一个修饰符用来测试多组数据,很有用。这些数据将会被传入到回调中作为参数。
test.each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
])('add(%i, %i) -> %i', (a, b, expected) => {
expect(a + b).toBe(expected)
})
// this will return
// ✓ add(1, 1) -> 2
// ✓ add(1, 2) -> 3
// ✓ add(2, 1) -> 3bench[15]: 目前还是体验版,如果你需要可以配置下,默认会找.{bench,benchmark}.{js,mjs,cjs等}的文件,如果你不想写这样的格式可以在配置文件中配置[16]。 这个api是用来测试性能的,通知vitest要运行多少次测试,然后会输出性能总结

而它相关的api就都跳过了,感兴趣的大佬可自行查看文档。
describe[17]: 如果你有需要将某个文件中的某几个测试元一并执行,那么这个将很有用。用describe接收两个参数,第一个是id,第二个是回调,回调中包裹你想要一起执行的测试元,这里就不多说。vitest bench -t test-describe
if (import.meta.vitest) {
const { bench, expect, describe } = import.meta.vitest
describe('test-describe', () => {
bench('test-bench', () => {
const x = [1, 5, 4, 2, 3]
x.sort((a, b) => {
return a - b
})
}, { time: 1000 })
bench('test_2', () => {
[1, 2].sort((a, b) => b - a)
})
})
}
-
expect[18]: 默认用的chai[19],也可以使用jest的, 这个就不多说了,用法和之前讲jest的差不多。 -
vi[20]:vitest暴露的一个工具类,提供了一些方法,感兴趣的大佬可以看下。
配置#
前面都有提到了,所以这里就不在重复,来看下这里面的一个配置deps[22],这个还是蛮重要的。

这是用来处理内联或者外部依赖的。
deps.external[23] : 哪些是外部依赖,默认是node_modules、/dist/,用过webpack等打包工具的大佬都清楚,这些是不参与打包编译处理的。deps.inline[24]: 和上边的相反,会被vite加工处理(只能是ESM)。
其他都是和jest类似的配置就不多说了。
实战DEMO#
差不多学完了,我们来起一个vite的vue项目。你也可以用官网的这个例子
如果你没有全局安装vite,建议安装一个,直接创建一个vite-vue的app,
cd ../
npm create vite vitest-vue --template vue
cd vitest-vue
npm install
npm install vitest -D然后跑下npm run dev看下是否正常。
然后创建vitest.config.js
echo vitest.config.js 然后配置下,当然你可以直接配置到vite的配置文件中,这样就不用写一些重复代码比如导入vue。
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
test: {
environment: 'jsdom',
globals: true
}
})我们测试的是vue组件,自然是client环境,所以environment得是jsdom。既然要用到jsdom,那就得下jsdom这个包。 然后vue的话我们用@vue/test-utils[25]这个包来测试组件,之前我们学jest的时候就用的这个包,感兴趣的大佬可以去翻看下。
npm install jsdom @vue/test-utils -D安装完之后我们来创建测试文件
mkdir __tests__
echo > __tests__/index.spec.js然后导入测试包和组件HelloWorld ,测试prop.msg传递是否正常
import { mount } from "@vue/test-utils";
import { describe, expect, it } from "vitest";
import HelloWorld from "../src/components/HelloWorld.vue";
describe("test-hello-world-props-msg", () => {
it("test", () => {
const wrapper = mount(HelloWorld, {
props: {
msg: "test",
},
});
expect(wrapper.get("h1").text()).contain(["test1"]);
});
});然后在package.json中配置下测试指令: "test": "vitest --run"
这样就能直接运行啦npm run test

正常运行。
那么到这就学完啦,如果觉得对你有用麻烦点个赞!谢谢~
参考#
- ^vite https://vitejs.dev/
- ^js-dom https://github.com/jsdom/jsdom
- ^vitest-cli https://vitest.dev/guide/cli.html#commands
- ^vitest-cli https://vitest.dev/guide/cli.html
- ^vitest-ui https://vitest.dev/guide/ui.html#vitest-ui
- ^in-source-testing https://vitest.dev/guide/in-source.html#in-source-testing
- ^vitest-environment https://vitest.dev/guide/environment.html
- ^vitest-matcher https://vitest.dev/guide/extending-matchers.html#extending-matchers
- ^vitest-debug https://vitest.dev/guide/debugging.html#debugging
- ^npm-ndb https://www.npmjs.com/package/ndb
- ^puppeteer https://docs.browserless.io/docs/puppeteer-library.html
- ^vitest-api https://vitest.dev/api/
- ^test.concurrent https://vitest.dev/api/#test-concurrent
- ^test-each https://vitest.dev/api/#test-each
- ^vitest-bench https://vitest.dev/api/#bench
- ^vitest-benchmark https://vitest.dev/config/#benchmark
- ^vitest-describe https://vitest.dev/api/#describe
- ^vitest-expect https://vitest.dev/api/#expect
- ^chai https://www.chaijs.com/
- ^vitest-vi https://vitest.dev/api/#vi
- ^vitest-configuration https://vitest.dev/config/#configuration
- ^vitest-configuration-deps https://vitest.dev/config/#deps
- ^deps-external https://vitest.dev/config/#deps-external
- ^deps-inline https://vitest.dev/config/#deps-inline
- ^@vue/test-utils https://test-utils.vuejs.org/guide/
发布于 2022-10-29 22:27
