人人都会AI编程

组件渲染测试、交互测试、快照测试

更新时间:2026-07-11

Vue 的测试体系离不开两个核心工具:Vitest(测试运行器)和 Vue Test Utils(官方组件测试工具库)。它们配合起来,可以让你像真实用户一样去渲染组件、触发交互、检查输出。下面分别讲三种最常用的测试类型。

渲染测试:验证组件是否正确渲染

渲染测试是最基础的测试,用来确认组件在给定 Props 和状态时,是否输出了预期的 DOM 结构或文本内容。

// 待测试组件 UserCard.vue
<template>
  <div class="user-card">
    <h3>{{ name }}</h3>
    <span v-if="isAdmin" class="badge">管理员</span>
  </div>
</template>

<script setup>
defineProps({
  name: String,
  isAdmin: Boolean
})
</script>
// UserCard.test.js
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import UserCard from './UserCard.vue'

describe('UserCard', () => {
  it('渲染用户名', () => {
    const wrapper = mount(UserCard, {
      props: { name: 'Alice', isAdmin: false }
    })
    expect(wrapper.text()).toContain('Alice')
  })

  it('当 isAdmin 为 true 时显示管理员徽章', () => {
    const wrapper = mount(UserCard, {
      props: { name: 'Bob', isAdmin: true }
    })
    expect(wrapper.find('.badge').exists()).toBe(true)
    expect(wrapper.find('.badge').text()).toBe('管理员')
  })

  it('当 isAdmin 为 false 时不显示管理员徽章', () => {
    const wrapper = mount(UserCard, {
      props: { name: 'Charlie', isAdmin: false }
    })
    expect(wrapper.find('.badge').exists()).toBe(false)
  })
})

关键点

  • mount 会渲染组件及其子组件,得到 wrapper 对象。
  • wrapper.text() 获取渲染后的纯文本内容。
  • wrapper.find(selector) 查找单个元素,.exists() 判断是否存在。
  • 通过传入不同的 Props,覆盖组件的各种分支状态。

交互测试:模拟用户操作并验证反馈

交互测试验证的是“用户点击按钮后,是否触发了预期的事件或状态变更”。Vue Test Utils 提供了 trigger 方法来模拟 DOM 事件。

// Counter.vue
<template>
  <div>
    <p data-testid="count">{{ count }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => { count.value++ }
</script>
// Counter.test.js
import { mount } from '@vue/test-utils'
import Counter from './Counter.vue'

describe('Counter', () => {
  it('点击按钮增加计数', async () => {
    const wrapper = mount(Counter)
    
    // 初始值为 0
    expect(wrapper.get('[data-testid="count"]').text()).toBe('0')
    
    // 模拟点击按钮
    await wrapper.find('button').trigger('click')
    
    // 点击后值变为 1
    expect(wrapper.get('[data-testid="count"]').text()).toBe('1')
  })

  it('多次点击累加计数', async () => {
    const wrapper = mount(Counter)
    const button = wrapper.find('button')
    
    await button.trigger('click')
    await button.trigger('click')
    await button.trigger('click')
    
    expect(wrapper.get('[data-testid="count"]').text()).toBe('3')
  })
})

关键点

  • trigger 是异步操作,需要使用 async/await,因为 Vue 会异步更新视图。
  • data-testid 属性来选取元素,比用 class 或标签更稳定,不易因样式调整而破坏测试。
  • 除了点击,还可以模拟输入:await wrapper.find('input').setValue('hello')

对于涉及异步请求的场景,可以 Mock 掉 Axios 或使用 Vitest 提供的 vi.fn() 来模拟函数调用,验证结果。

快照测试:防止非预期的 UI 变更

快照测试会记录组件第一次渲染时的 DOM 结构,后续测试会对比当前输出是否与快照一致。如果出现差异,测试会失败,提醒你要么更新了合法的 UI,要么意外改动破坏了原有结构。

// UserCard.test.js 快照测试
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import UserCard from './UserCard.vue'

describe('UserCard 快照', () => {
  it('渲染结果与快照一致', () => {
    const wrapper = mount(UserCard, {
      props: { name: 'Alice', isAdmin: true }
    })
    // 生成/对比快照
    expect(wrapper.html()).toMatchSnapshot()
  })
})

首次运行测试时,Vitest 会在 snapshots 目录下生成一个快照文件,内容类似:

// UserCard.test.js.snap
exports[`UserCard 快照 > 渲染结果与快照一致 1`] = `
"<div class="user-card">
  <h3>Alice</h3>
  <span class="badge">管理员</span>
</div>"
`;

之后每次运行测试,Vitest 都会将当前输出与这个文件对比。如果你有意修改了 UI(比如增加了 .vip 样式),测试会失败,此时可以选择更新快照:

npx vitest --update

快照测试的注意事项

  • 快照不是“保证正确”,而是“防止意外改动”。不要盲目更新快照,需确认差异是你意图内的变更。
  • 避免过于庞大的快照。只对关键组件或小组件做快照测试,不要把一个含几百行 HTML 的页面整体快照,否则任何微小改动都会导致快照失效,失去保护意义。
  • 配合其他测试使用。快照测试不能替代行为测试,它只是告诉你“长相没变”,不能验证交互逻辑。

总结:渲染测试确保组件正确显示,交互测试验证行为逻辑,快照测试保护 UI 结构不被意外修改。三者结合,可以覆盖组件开发中绝大多数的质量问题,让重构和协作更有信心。