Appearance
Vitest
简介
Vitest 是一个由 Vite 提供支持的极速单元测试框架,专为现代前端项目设计。它利用 Vite 的开发服务器和 HMR(热模块替换)能力,提供了极快的测试执行速度和优秀的开发体验。Vitest 在 API 设计上与 Jest 兼容,同时提供了更好的性能和更现代的特性。
核心特性
极速执行
Vitest 利用 Vite 的按需编译和原生 ESM 支持,实现了极快的测试启动和执行速度:
bash
# 安装 Vitest
npm install -D vitest
# 添加测试脚本到 package.json
{
"scripts": {
"test": "vitest",
"test:ui": "vitest --ui",
"test:run": "vitest run"
}
}
# 运行测试
npm test
智能监视模式
Vitest 默认以监视模式运行,只重新运行受影响的测试,大大提高了开发效率:
bash
# 启动监视模式
npm test
# 运行一次后退出
npm test:run
内置 UI 界面
Vitest 提供了一个现代化的 Web UI 界面,可以可视化测试结果和覆盖率报告:
bash
npm test:ui
基本用法
编写测试
javascript
// sum.js
export function sum(a, b) {
return a + b;
}
// sum.test.js
import { expect, test } from 'vitest';
import { sum } from './sum';
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
测试组织
javascript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
describe('计算器功能测试', () => {
beforeEach(() => {
// 每个测试前的设置
});
afterEach(() => {
// 每个测试后的清理
});
it('加法运算', () => {
expect(sum(1, 2)).toBe(3);
});
it('减法运算', () => {
expect(subtract(5, 2)).toBe(3);
});
});
常用匹配器
Vitest 提供了与 Jest 兼容的匹配器 API:
javascript
// 精确匹配
expect(value).toBe(2); // 使用 Object.is 比较
expect(value).toEqual({ a: 1 }); // 递归比较对象的值
// 真值检查
expect(value).toBeTruthy();
expect(value).toBeFalsy();
expect(value).toBeNull();
expect(value).toBeUndefined();
// 数字比较
expect(value).toBeGreaterThan(3);
expect(value).toBeLessThanOrEqual(5);
// 字符串
expect('team').toMatch(/tea/);
// 数组
expect(['apple', 'banana']).toContain('apple');
// 异常
expect(() => { throw new Error('错误') }).toThrow();
高级功能
异步测试
javascript
// Promise
test('异步数据获取', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
});
});
// Async/Await
test('异步数据获取', async () => {
const data = await fetchData();
expect(data).toBe('peanut butter');
});
测试覆盖率
Vitest 内置了基于 c8/v8 的代码覆盖率工具:
bash
# 运行测试并收集覆盖率
npm test -- --coverage
javascript
// vitest.config.js
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
coverage: {
provider: 'c8', // 或 'istanbul'
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'test/'
],
lines: 90,
functions: 90,
branches: 90,
statements: 90
}
}
});
测试 Vue 组件
javascript
import { mount } from '@vue/test-utils';
import { describe, it, expect } from 'vitest';
import Counter from '../Counter.vue';
describe('Counter.vue', () => {
it('increments count when button is clicked', async () => {
const wrapper = mount(Counter);
expect(wrapper.text()).toContain('Count: 0');
await wrapper.find('button').trigger('click');
expect(wrapper.text()).toContain('Count: 1');
});
});
测试 React 组件
javascript
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import Counter from '../Counter';
describe('Counter', () => {
it('increments count when button is clicked', () => {
render(<Counter />);
expect(screen.getByText('Count: 0')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button'));
expect(screen.getByText('Count: 1')).toBeInTheDocument();
});
});
配置选项
vitest.config.js
javascript
import { defineConfig } from 'vitest/config';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
test: {
// 测试环境
environment: 'jsdom',
// 包含的文件
include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
// 排除的文件
exclude: ['**/node_modules/**', '**/dist/**'],
+
// 全局测试设置
globals: true,
// 测试超时时间(毫秒)
testTimeout: 5000,
// 并行或串行运行
threads: true,
// 模拟浏览器环境
deps: {
inline: ['element-plus']
},
// 别名
alias: {
'@': '/src'
}
}
});
与 Vite 集成
Vitest 可以直接使用 Vite 的配置,包括插件、别名和转换器:
javascript
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': '/src'
}
},
// Vitest 会自动使用这些配置
});
最佳实践
测试组织
- 使用
describe
对相关测试进行分组 - 使用
beforeEach
和afterEach
设置和清理测试环境 - 利用
it.skip
和it.todo
管理测试进度
性能优化
- 启用多线程执行:
--threads
- 使用
--bail
在首次失败后停止 - 利用
--update
自动更新快照
Mock 策略
- 使用
vi.mock()
模拟模块 - 使用
vi.fn()
创建模拟函数 - 使用
vi.spyOn()
监视对象方法
javascript
import { vi } from 'vitest';
// 模拟函数
const mockFn = vi.fn();
mockFn.mockReturnValue(42);
// 模拟模块
vi.mock('./math', () => {
return {
sum: vi.fn().mockReturnValue(10)
};
});
// 监视方法
const spy = vi.spyOn(console, 'log');
常见问题与解决方案
与 Jest 的兼容性
javascript
// vitest.config.js
export default {
test: {
globals: true, // 启用全局 API,无需导入
environment: 'jsdom', // 使用 jsdom 环境
setupFiles: ['./setup.js'] // 设置文件,类似 Jest 的 setupFilesAfterEnv
}
};
模块路径解析
javascript
// vitest.config.js
import { defineConfig } from 'vitest/config';
import { resolve } from 'path';
export default defineConfig({
resolve: {
alias: {
'@': resolve(__dirname, './src')
}
}
});
测试环境问题
bash
# 安装 jsdom 环境
npm install -D @vitest/environment-jsdom
# 安装 happy-dom 环境(更轻量的替代品)
npm install -D happy-dom
javascript
// vitest.config.js
export default {
test: {
environment: 'jsdom', // 或 'happy-dom', 'node'
}
};
与其他测试工具对比
特性 | Vitest | Jest | Cypress |
---|---|---|---|
执行速度 | 极快 | 中等 | 慢 |
Vite 集成 | 原生 | 需配置 | 需配置 |
配置复杂度 | 低 | 中等 | 高 |
内置 UI | 支持 | 不支持 | 支持 |
组件测试 | 优秀 | 良好 | 优秀 |
热更新 | 支持 | 有限 | 不支持 |
社区生态 | 成长中 | 丰富 | 丰富 |