一、前言

你将从本文章了解到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工作。

 

更多推荐