前端单元测试快速上手:Vitest测试Vue/React组件
一、前言
你将从本文章了解到Vitest对于Vue/React组件的单元测试。
实际开发中:建议搭配AI如Claude快速出测试用例,提高开发效率。
(用Claude的原因是其上下文比其他AI长,后续也可能版本迭代或者出现新AI,会有更好的选择,AI选择这一点看个人)
二、快速搭建测试环境
要求:Node ≥ 18.x 稳定版
不同的版本可能出现不一样的配置,请结合实际情况配置哦
VUE3如下:
pnpm add -D vitest @vue/test-utils jsdom
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
test: {
// 模拟浏览器环境
environment: 'jsdom',
// 开启全局 API,无需每次 import
globals: true,
// 匹配测试文件
include: ['**/*.{test,spec}.{js,ts}'],
// 关键!解决 Vue 组件编译报错
deps: {
inline: ['vue']
}
}
})
REACT如下:
pnpm add -D vitest @testing-library/react @testing-library/jest-dom jsdom
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
globals: true,
include: ['**/*.{test,spec}.{js,ts,jsx,tsx}']
}
})
三、API
1、核心 API
-
describe:测试套件,归类同一组件测试用例
-
it:单个测试用例,对应一个功能点
-
expect:断言,判断结果是否符合预期
-
vi.fn():模拟函数,监听函数执行状态
-
includes():文本匹配断言,判断组件渲染文本是否包含指定内容
-
toBeTruthy():判断结果为真,用于校验事件是否成功触发
-
toBeUndefined():判断变量/事件不存在,用于校验禁用状态下事件不触发
-
toBeInTheDocument():DOM 断言,判断元素成功渲染到页面
-
toHaveBeenCalledTimes(n):校验模拟函数执行次数,精准判断点击回调触发次数
2、框架专属测试 API
-
Vue:mount():挂载 Vue 单文件组件,返回组件实例 wrapper,用于获取文本、触发事件
-
Vue:wrapper.trigger('click'):模拟用户点击、输入等原生 DOM 事件
-
Vue:wrapper.emitted('xxx'):监听组件自定义事件是否触发
-
React:render():渲染 React 组件到测试 DOM
-
React:screen.getByText():通过文本获取页面 DOM 元素
-
React:fireEvent.click():模拟用户真实点击交互
四、实战1:Vitest 测试 Vue3 组件
待测试组件如下:
<template>
<button :disabled="disabled" @click="handleClick">{{ text }}</button>
</template>
<script setup lang="ts">
const props = defineProps<{ text: string; disabled?: boolean }>()
const emit = defineEmits(['click'])
const handleClick = () => {
!props.disabled && emit('click')
}
</script>
测试文件如下:
import { mount } from '@vue/test-utils'
import MyButton from './MyButton.vue'
describe('Vue按钮组件测试', () => {
// 测试文本渲染
it('正确渲染按钮文本', () => {
const wrapper = mount(MyButton, { props: { text: '提交' } })
expect(wrapper.text()).includes('提交')
})
// 测试正常点击事件
it('正常状态点击触发事件', async () => {
const wrapper = mount(MyButton, { props: { text: '点击' } })
await wrapper.trigger('click')
expect(wrapper.emitted('click')).toBeTruthy()
})
// 测试禁用状态拦截点击
it('禁用状态不触发点击事件', async () => {
const wrapper = mount(MyButton, { props: { text: '禁用', disabled: true } })
await wrapper.trigger('click')
expect(wrapper.emitted('click')).toBeUndefined()
})
})
五、实战2:Vitest 测试 React 组件
待测试组件如下:
interface Props {
text: string
disabled?: boolean
onClick?: () => void
}
export default function MyButton({ text, disabled, onClick }: Props) {
return <button disabled={disabled} onClick={onClick}>{text}</button>
}
测试文件如下:
import { render, screen, fireEvent } from '@testing-library/react'
import MyButton from './MyButton'
describe('React按钮组件测试', () => {
it('正确渲染文本', () => {
render(<MyButton text="确认" />)
expect(screen.getByText('确认')).toBeInTheDocument()
})
it('点击触发回调', () => {
const fn = vi.fn()
render(<MyButton text="点击" onClick={fn} />)
fireEvent.click(screen.getByText('点击'))
expect(fn).toHaveBeenCalledTimes(1)
})
it('禁用拦截点击回调', () => {
const fn = vi.fn()
render(<MyButton text="禁用" disabled onClick={fn} />)
fireEvent.click(screen.getByText('禁用'))
expect(fn).not.toHaveBeenCalled()
})
})
六、总结
最后总结还是想说,拥抱AI,在写测试用例时候配合AI效率会大大提高,但是一定要做好review工作。
更多推荐



所有评论(0)