概念
工作空间
: Vitest 通过工作空间配置文件提供了对monorepos
(一个仓库管理多个项目的策略) 的内置支持。你可以创建一个工作空间来定义项目的配置。
在仓库根目录创建工作空间配置, 即使文件夹中没有配置文件,Vitest 也会将 packages 文件夹中的每个文件夹视为单独的项目
// vitest.workspace.ts
export default ['packages/*']
报告器
: Vitest 提供了几种内置报告器,可以以不同格式显示测试输出的内容。
//...
export default defineConfig({
test: {
reporters: ['verbose'], //详细报告器
}
})
内联测试
: Vitest 支持 在源码中进行测试套件的编辑
// 函数实现
export function add(...args: number[]) {
return args.reduce((a, b) => a + b, 0)
}
// 源码内的测试套件
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)
})
}
语法
test 别名 it
test 用于验证代码的正确性 , test 定义了一组关于测试期望的方法。它接收测试名称(类似备注名称)和一个含有测试期望的函数,同时,可以提供一个超时时限(以毫秒为单位)用于指定等待多长时间后终止测试,默认为 5 秒
__test__/index.js//示例
import { test, assert } from 'vitest'
const isDev = process.env.NODE_ENV === "development"; //是否在测试环境
test('name', async()=>{/* ... */},1000) //支持异步
test.skip('name',()=>{/* ... */}) //跳过测试
//如果想跳过运行某些测试,但又不想删代码,可以使用 test.skip 来跳过这些测试
test.skipIf(isDev)('name',()=>{/* ... */}) //根据条件跳过测试
//如果想在开发环境跳过某些测试,可以使用 test.skipIf 来跳过这些测试
test.runIf(isDev)('name',()=>{/* ... */}) //根据条件运行测试
//如果只想在开发环境运行某些测试,可以使用 test.runIf 来跳过这些测试
test.only('name',()=>{/*...*/}) //只调试这条测试代码
//如果想调试某些测试,其他测试不运行,则可以使用 test.only;生产环境不保留test.only
test.concurrent("name", async () => { }); //并行运行异步测试
test.concurrent("name", async () => { }); //并行运行异步测试
//test.skip、test.only 和 test.todo 适用于并发测试
test.skip.concurrent(/* ... */) // or test.concurrent.skip(/* ... */)
test.todo('存根') //存根测试
test.fails()
test.each()
expect
expect 用来创建断言。在当前上下文中,可以使用 assertions 方法来断言一个语句。 Vitest 默认提供 chai 断言,同时基于 chai 实现兼容 Jest 的断言语句
__test__/index.js//示例
import {expect} from 'vitest'
const input = 2
expect(input).to.equal(2) // chai 风格
expect(input).toBe(2) // jest 风格
expect(input).toBe(2) //断言 input 是 2
//也就是简单的,我敢说某某是某某。不是就会报错
expect.sort() //失败不终止,继续执行
expect.not() //否定断言
expect.assertions(2); //断言异步调用了两次
expect.hasAssertions(); //断言异步调用了至少一次
expect.unreachable(); //断言永不执行
expect.anything
expect.any
expect.arrayContaining
expect.objectContaining
expect.stringContaining
expect.stringMatching
expect.addSnapshotSerializer
expect.extend
expect().toBe() //相同断言 (禁止使用浮点型)
expect().toBeCloseTo() //相同断言 (浮点型)
expect().toBeUndefined() //断言无返回 (或返回undefined)
expect().toBeDefined() //断言有返回
expect().toBeTruthy() //断言结果为真(true)
expect().toBeFalsy() //断言结果为假(false)
expect().toBeNull() //断言为null
expect().toBeNaN() //断言为NaN
expect().toBeTypeOf(); //类型断言
expect().toBeInstanceOf(); //断言父类
expect(5).toBeGreaterThan(1); //断言 5 大于 1
expect(5).toBeGreaterThanOrEqual(1); //断言 5 大于等于 1
expect(1).toBeLessThan(5); //断言 1 小于 5
expect(1).toBeLessThanOrEqual(5); //断言 1 小于等于 5
expect().toEqual()
expect().toStrictEqual()
expect().toContain()
expect().toContainEqual()
expect().toHaveLength()
expect().toHaveProperty()
expect().toMatch()
expect().toMatchObject()
expect().toThrowError()
expect().toMatchSnapshot()
expect().toMatchInlineSnapshot()
expect().toThrowErrorMatchingSnapshot()
expect().toThrowErrorMatchingInlineSnapshot()
expect().toHaveBeenCalled() //断言函数被调用,需要spy函数
expect().toHaveBeenCalledTimes()
expect().toHaveBeenCalledWith()
expect().toHaveBeenLastCalledWith()
expect().toHaveBeenNthCalledWith()
expect().toHaveReturned()
expect().toHaveReturnedTimes()
expect().toHaveReturnedWith()
expect().toHaveLastReturnedWith()
expect().toHaveNthReturnedWith()
expect().toSatisfy()
expect().resolves()
expect().rejects()
bench
仅在bench模式下可用,bench 用于评估代码的性能 , bench
定义了一个基准。 在 Vitest 术语中,基准是定义一系列操作的函数。 Vitest 多次运行此函数以显示不同的性能结果
//示例
import { bench } from "vitest";
bench("name",() => {},{ time: 1000 }); //多次执行,用于性能测试
bench.skip('name',()=>{/* ... */}) //跳过测试
//如果想跳过运行某些测试,但又不想删代码,可以使用 bench.skip 来跳过这些测试
bench.only('name',()=>{/*...*/}) //只调试这条测试代码
//如果想调试某些测试,其他测试不运行,则可以使用 bench.only;生产环境不保留bench.only
bench.todo('存根') //使用 bench.todo 来存根基准,以便以后实施
describe 套件
当你在文件的顶层使用 test 或 bench 时,它们会被收集为它的隐式套件的一部分。 使用 describe 你可以在当前上下文中定义一个新套件,作为一组相关的测试或基准以及其他嵌套套件
import { test,describe } from 'vitest'
import { testName } from '../src/index'
const person = {
isActive: true,
age: 32,
};
describe("person", () => { //类似一个嵌套结构,仅用于展示效果
test("person is defined", () => {
expect(person).toBeDefin ed();
});
})
// describe.skip 跳过
describe.skip("skipped suite", () => {
test("sqrt", () => {
// 跳过测试套件,不会有错误
assert.equal(Math.sqrt(4), 3);
});
});
// describe.only 只运行里面的,其他的不运行
describe.only("skipped suite", () => { //执行
test("sqrt", () => {
assert.equal(Math.sqrt(4), 3);
});
});
test("sqrt", () => { //不执行
assert.equal(Math.sqrt(4), 3);
});
describe.concurrent
describe.shuffle
describe.todo
describe.each
Vi 工具函数
// vi.advanceTimersByTime 定时停止
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersByTime(150); // 150 毫秒后停止执行
vi.advanceTimersToNextTimer
vi.clearAllMocks
vi.clearAllTimers
vi.dynamicImportSettled
vi.fn
vi.getMockedSystemTime
vi.getRealSystemTime
vi.mock
vi.mocked
vi.importActual
vi.importMock
vi.resetAllMocks
vi.resetConfig
vi.resetModules
vi.restoreAllMocks
vi.restoreCurrentDate
vi.runAllTicks
vi.runAllTimers
vi.runOnlyPendingTimers
vi.setSystemTime
vi.setConfig
vi.spyOn
vi.stubGlobal
vi.unmock
vi.useFakeTimers
vi.useRealTimers
MockInstance Methods
getMockName
mockClear
mockName
mockImplementation
mockImplementationOnce
mockRejectedValue
mockRejectedValueOnce
mockReset
mockRestore
mockResolvedValue
mockResolvedValueOnce
mockReturnThis
mockReturnValue
mockReturnValueOnce
MockInstance Properties
mock.calls
mock.lastCall
mock.results
mock.instances
测试代码
快照测试
安装插件 @testing-library
由于涉及到i8n国际翻译插件,需要进行配置,不能直接调用快照,不然会报错。
// __test__/index.test.ts
import {expect,test,vi} from 'vitest'
import {render} from '@testing-library/vue'
import LoginPage from '../src/pages/login/index.vue'
import {i18n} from '../src/modules/i18n'
test('Login 快照',()=>{
const { container } = render(LoginPage,{
global: {
plugins:[i18n]
}
})
expect(container).toMatchSnapshot()
})
如果涉及到 element-plus 组件,那么也需要进行配置,不然会报错;
//vite.config.js
test:{
//...
server:{
deps: {
inline: ['element-plus'],
},
}
},
//src/modules/i18n
import { App } from 'vue'
import { createI18n } from 'vue-i18n'
const messages = Object.fromEntries(
Object.entries(import.meta.glob<{ default: any }>('../language/*.y(a)?ml',{ eager: true }))
.map(([key, value]) => {
const yaml = key.endsWith('.yaml')
return [key.slice(12, yaml ? -5 : -4), value.default]
}),
)
export const i18n = createI18n({
legacy: false,
locale: 'en',
messages,
})
export const install = (app:App) => {
app.use(i18n)
}
export default install
快照的更新命令,如果快照不一致,但是因为需求的改动,确实快照变了,那么需要更新快照。
vitest -u
执行命令
vitest //监听命令
Vitest base //筛选命令,只执行以 base 开头的测试文件