第 27 章:测试
学习目标
- 配 Vitest 跑单元测试
- 用 Vue Test Utils 测组件
- 用 Pinia 测试 store
- 避坑:异步测试与 mock
一、安装
bash
npm i -D vitest @vue/test-utils jsdom @vitest/coverage-v8ts
// 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:covts
// package.json
"scripts": {
"test": "vitest",
"test:cov": "vitest --coverage"
}⚠️ 坑 2:
@vue/test-utils的mount默认会创建真实组件,大数据记得 stub。
九、e2e(可选)
bash
npm i -D cypressts
// 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 | 组件挂载 |
| Pinia | store 测试 |
| Cypress | e2e |
动手练习
- Counter:测 + / - / reset 三个按钮
- API mock:用
vi.mockmock userApi,测 UserList - store:给 userStore 写登录测试
下一章:第 28 章:部署 →