Skip to content
第 27 章 前端 ⏱ 13 分钟阅读

第 27 章:测试 ​

学习目标 ​

  • 配 Vitest 跑单元测试
  • 用 Vue Test Utils 测组件
  • 用 Pinia 测试 store
  • 避坑:异步测试与 mock

一、安装 ​

bash
npm i -D vitest @vue/test-utils jsdom @vitest/coverage-v8
ts
// vite.config.ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    environment: 'jsdom',
    globals: true,
    setupFiles: ['./tests/setup.ts'],
  },
})

二、setup.ts ​

ts
import { config } from '@vue/test-utils'

config.global.stubs = { transition: true }

三、组件测试 ​

ts
// src/components/__tests__/Counter.spec.ts
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import Counter from '../Counter.vue'

describe('Counter', () => {
  it('renders initial count', () => {
    const wrapper = mount(Counter, { props: { initial: 5 } })
    expect(wrapper.text()).toContain('5')
  })

  it('increments on click', async () => {
    const wrapper = mount(Counter, { props: { initial: 0 } })
    await wrapper.find('button').trigger('click')
    expect(wrapper.text()).toContain('1')
  })
})
vue
<!-- Counter.vue -->
<script setup lang="ts">
const props = defineProps<{ initial: number }>()
const count = ref(props.initial)
</script>
<template>
  <button @click="count++">{{ count }}</button>
</template>

四、异步组件 ​

ts
it('loads data', async () => {
  const wrapper = mount(UserList)
  await flushPromises()
  expect(wrapper.findAll('.user').length).toBeGreaterThan(0)
})
ts
import { flushPromises } from '@vue/test-utils'

五、Mock 模块 ​

ts
import { vi } from 'vitest'

vi.mock('@/api/user', () => ({
  userApi: {
    list: vi.fn().mockResolvedValue({ items: [{ id: 1, name: 'A' }], total: 1 }),
  },
}))

⚠️ 坑 1:vi.mock 必须放在 import 之前,或用 vi.doMock 动态。

六、Pinia 测试 ​

ts
import { setActivePinia, createPinia } from 'pinia'
import { useUserStore } from '@/stores/user'

beforeEach(() => setActivePinia(createPinia()))

it('login', async () => {
  const user = useUserStore()
  await user.login('alice', '123')
  expect(user.token).toBe('123')
})

七、组合式测试 ​

ts
import { useCounter } from '@/composables/useCounter'

it('counter', () => {
  const { count, inc } = useCounter(10)
  inc()
  expect(count.value).toBe(11)
})

八、覆盖率 ​

bash
npm run test:cov
ts
// package.json
"scripts": {
  "test": "vitest",
  "test:cov": "vitest --coverage"
}

⚠️ 坑 2:@vue/test-utils 的 mount 默认会创建真实组件,大数据记得 stub。

九、e2e(可选) ​

bash
npm i -D cypress
ts
// cypress/e2e/login.cy.ts
it('login', () => {
  cy.visit('/login')
  cy.get('input[name=username]').type('alice')
  cy.get('input[name=password]').type('123')
  cy.get('button').click()
  cy.url().should('include', '/')
})

十、本章小结 ​

工具用途
Vitest单元测试
Vue Test Utils组件挂载
Piniastore 测试
Cypresse2e

动手练习 ​

  1. Counter:测 + / - / reset 三个按钮
  2. API mock:用 vi.mock mock userApi,测 UserList
  3. store:给 userStore 写登录测试

下一章:第 28 章:部署 →

本站基于 VitePress 构建 · 由 StackHub 团队维护