Skip to main content

Vitest 测试框架基本用法

一、Vitest 的安装及配置

1、安装

yarn add -D vitest

package.json 中添加:

package.json
{
"scripts": {
"test": "vitest"
}
}

2、基本用法

sum.js
function sum(a, b) {
return a + b;
}

export default sum

运行 yarn test,终端输出:

Vitest 的默认匹配规则与 jest 相似,可通过根目录下的 vite.config.tsinclude 自定义匹配:

vite.config.ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
include: // ...
},
})

二、Vitest 常用的匹配器

1、相等

toBe() 用来测试两个值是否相等:

expect(2 + 2).toBe(4);

toEqual() 会递归检查数组或对象的每个字段,用来测试两个对象是否相等:

const data = {one: 1};
data['two'] = 2;
expect(data).toEqual({one: 1, two: 2});

2、数字

数字大小的比较可以通过以下匹配器来进行匹配:

test('two plus two', () => {
const value = 2 + 2;
expect(value).toBeGreaterThan(3);
expect(value).toBeGreaterThanOrEqual(3.5);
expect(value).toBeLessThan(5);
expect(value).toBeLessThanOrEqual(4.5);

// toBe and toEqual are equivalent for numbers
expect(value).toBe(4);
expect(value).toEqual(4);
});

对于浮点数的比较,需使用 toBeCloseTo 而非 toBe,否则会导致舍入误差:

test('两个浮点数字相加', () => {
const value = 0.1 + 0.2;
// expect(value).toBe(0.3); 这句会报错,因为浮点数有舍入误差
expect(value).toBeCloseTo(0.3); // 这句可以运行
});

3、取反

not 可以表达相反匹配:

expect(a + b).not.toBe(0);

4、真值

可以使用以下真值判断 undefined, null, false 等:

test('null', () => {
const n = null;
expect(n).toBeNull();
expect(n).toBeDefined();
expect(n).not.toBeUndefined();
expect(n).not.toBeTruthy();
expect(n).toBeFalsy();
});

test('zero', () => {
const z = 0;
expect(z).not.toBeNull();
expect(z).toBeDefined();
expect(z).not.toBeUndefined();
expect(z).not.toBeTruthy();
expect(z).toBeFalsy();
});

5、包含

可以通过 toContain 来检查一个数组或可迭代对象是否包含某个特定项:

const shoppingList = [
'diapers',
'kleenex',
'trash bags',
'paper towels',
'milk',
];

test('shoppingList 数组中包含 milk', () => {
expect(shoppingList).toContain('milk');
expect(new Set(shoppingList)).toContain('milk');
});

三、模拟对象

vitest 的 vi 提供了一系列的模拟对象:

1、模拟日期

import { it, vi } from 'vitest'

beforeEach(() => {
// 告诉 vitest 使用模拟时间
vi.useFakeTimers()
})

it('set system time', () => {
// 在工作时间内设置时间
const date = new Date(2000, 1, 1, 13)
vi.setSystemTime(date)
// ...
})

afterEach(() => {
// 每次测试运行后恢复日期
vi.useRealTimers()
})

2、模拟函数

import { mount } from '@vue/test-utils';
import { it, expect, vi } from 'vitest';
import Button from '@/src/button/index';

it('onClick', () => {
const clickFn = vi.fn();
const wrapper = mount({
render() {
return <Button onClick={clickFn} />;
},
});
wrapper.findComponent(Button).trigger('click');
expect(clickFn).toHaveBeenCalled();
});

点击查看更多用法

四、测试钩子

1、重复运行

beforeEach 可以在文件中每个测试用例开始前做一些预执行工作,afterEach 可以在每个测试用例结束后进行一些清理工作:

beforeEach(() => {
initializeCityDatabase();
});

afterEach(() => {
clearCityDatabase();
});

test('city database has Vienna', () => {
expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
expect(isCity('San Juan')).toBeTruthy();
});

2、一次性运行

beforeAllafterAll 可以在文件中所有测试用例开始前和结束后一次性执行,而不用每个测试用例都执行一遍,从而影响性能:

beforeAll(() => {
return initializeCityDatabase();
});

afterAll(() => {
return clearCityDatabase();
});

test('city database has Vienna', () => {
expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
expect(isCity('San Juan')).toBeTruthy();
});

3、作用域

可以通过 describe 块来将测试用例分组:

beforeAll(() => console.log('1 - beforeAll'));
afterAll(() => console.log('1 - afterAll'));
beforeEach(() => console.log('1 - beforeEach'));
afterEach(() => console.log('1 - afterEach'));

test('', () => console.log('1 - test'));

describe('Scoped / Nested block', () => {
beforeAll(() => console.log('2 - beforeAll'));
afterAll(() => console.log('2 - afterAll'));
beforeEach(() => console.log('2 - beforeEach'));
afterEach(() => console.log('2 - afterEach'));

test('', () => console.log('2 - test'));
});

输出: