技术文档中心
首页
React
Vue
TypeScript
Kotlin
React Native
Electron
Android
首页
React
Vue
TypeScript
Kotlin
React Native
Electron
Android
  • Vue2 基础

    • Vue 学习指南
    • Vue2 快速入门
    • Vue2 模板语法
    • Vue2 组件基础
    • Vue2 计算属性与侦听器
    • Vue2 生命周期
  • Vue2 进阶

    • Vue2 组件通信
    • Vue2 插槽详解
    • Vue2 混入与自定义指令
    • Vue2 过渡与动画
    • Vuex 状态管理
  • Vue2 高级

    • Vue2 性能优化
    • Vue2 服务端渲染(SSR)
    • Vue2 源码解析
  • Vue3 基础

    • Vue3 快速入门
    • Vue3 Composition API
    • Vue3 响应式系统
    • Vue3 组件基础
    • Vue3 生命周期
  • Vue3 进阶

    • Vue3 组合式函数
    • Vue3 组件通信
    • Vue3 Teleport 与 Suspense
    • Pinia 状态管理
    • Vue Router 4
  • Vue3 高级

    • Vue3 TypeScript
    • Vue3 性能优化
    • Vue3 测试
    • Vue3 SSR
    • Vue3 源码解析
  • 实战项目

    • Vue2 实战项目
    • Vue3 实战项目
    • Vue2 迁移 Vue3

Vue3 测试

测试工具

Vitest

yarn add -D vitest @vue/test-utils
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  test: {
    environment: 'jsdom'
  }
})

组件测试

基本测试

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

describe('MyComponent', () => {
  it('renders properly', () => {
    const wrapper = mount(MyComponent, {
      props: { message: 'Hello' }
    })
    expect(wrapper.text()).toContain('Hello')
  })
})

测试事件

it('emits event when button clicked', async () => {
  const wrapper = mount(MyComponent)
  await wrapper.find('button').trigger('click')
  
  expect(wrapper.emitted()).toHaveProperty('submit')
  expect(wrapper.emitted('submit')).toHaveLength(1)
})

测试 Props

it('accepts props', () => {
  const wrapper = mount(MyComponent, {
    props: {
      title: 'Test Title',
      count: 10
    }
  })
  
  expect(wrapper.props('title')).toBe('Test Title')
  expect(wrapper.props('count')).toBe(10)
})

测试插槽

it('renders slot content', () => {
  const wrapper = mount(MyComponent, {
    slots: {
      default: '<div>Slot Content</div>'
    }
  })
  
  expect(wrapper.html()).toContain('Slot Content')
})

测试组合式函数

// useMouse.spec.ts
import { describe, it, expect } from 'vitest'
import { useMouse } from './useMouse'

describe('useMouse', () => {
  it('returns mouse position', () => {
    const { x, y } = useMouse()
    
    expect(x.value).toBe(0)
    expect(y.value).toBe(0)
  })
})

测试 Pinia Store

// counter.spec.ts
import { setActivePinia, createPinia } from 'pinia'
import { describe, it, expect, beforeEach } from 'vitest'
import { useCounterStore } from './counter'

describe('Counter Store', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
  })

  it('increments counter', () => {
    const counter = useCounterStore()
    expect(counter.count).toBe(0)
    
    counter.increment()
    expect(counter.count).toBe(1)
  })
})

测试路由

import { mount } from '@vue/test-utils'
import { createRouter, createMemoryHistory } from 'vue-router'

const router = createRouter({
  history: createMemoryHistory(),
  routes: [
    { path: '/', component: Home },
    { path: '/about', component: About }
  ]
})

it('navigates to about page', async () => {
  router.push('/about')
  await router.isReady()
  
  const wrapper = mount(App, {
    global: {
      plugins: [router]
    }
  })
  
  expect(wrapper.html()).toContain('About')
})

Mock

Mock 函数

import { vi } from 'vitest'

it('calls callback', () => {
  const callback = vi.fn()
  const wrapper = mount(MyComponent, {
    props: { onSubmit: callback }
  })
  
  wrapper.find('button').trigger('click')
  expect(callback).toHaveBeenCalled()
})

Mock API

import { vi } from 'vitest'

global.fetch = vi.fn(() =>
  Promise.resolve({
    json: () => Promise.resolve({ data: 'test' })
  })
)

it('fetches data', async () => {
  const wrapper = mount(MyComponent)
  await wrapper.vm.$nextTick()
  
  expect(fetch).toHaveBeenCalled()
})

快照测试

it('matches snapshot', () => {
  const wrapper = mount(MyComponent, {
    props: { message: 'Hello' }
  })
  
  expect(wrapper.html()).toMatchSnapshot()
})

E2E 测试

Playwright

yarn add -D @playwright/test
// e2e/example.spec.ts
import { test, expect } from '@playwright/test'

test('homepage has title', async ({ page }) => {
  await page.goto('http://localhost:5173')
  await expect(page).toHaveTitle(/My App/)
})

test('can navigate to about page', async ({ page }) => {
  await page.goto('http://localhost:5173')
  await page.click('text=About')
  await expect(page).toHaveURL(/about/)
})
最近更新: 2026/1/27 15:51
Contributors: hailong
Prev
Vue3 性能优化
Next
Vue3 SSR