前端组件测试是保证UI质量和组件库稳定的基础手段。React/Vue组件逻辑复杂、交互多,只靠手工点测很难覆盖边界情况,回归一次漏一次。组件测试的常规分层:单元测试验证组件逻辑和渲染,集成测试验证组件间交互,端到端测试验证真实浏览器里的完整流程。Vitest基于Vite生态,启动快、API兼容Jest,是当前Vue3和React项目的主流选择;Playwright负责端到端测试,直接驱动Chromium、Firefox、WebKit真实浏览器。本文给出这套方案的落地配置。
Vitest单元测试环境搭建与配置
Vitest对Vite项目几乎零配置接入。npm安装vitest和配套库:@testing-library/react(React)或@vue/test-utils(Vue),再加上jsdom环境模拟浏览器API。
# 安装依赖
npm install -D vitest @testing-library/react @testing-library/jest-dom jsdom @vitejs/plugin-react
// vite.config.ts 增加测试配置
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: './src/test/setup.ts',
coverage: {
provider: 'v8',
reporter: ['text', 'html'],
include: ['src/components/**/*.{ts,tsx}'],
exclude: ['src/**/*.d.ts']
}
}
})
package.json里加一行:npm test跑单元测试,npm run coverage看覆盖率。setup.ts里引入@testing-library/jest-dom/vitest把toBeInTheDocument这类断言注册进全局。
组件单元测试实践:渲染、交互与状态断言
单元测试的核心是验证组件在给定props下渲染正确,交互事件触发后行为符合预期。React Testing Library推荐的写法是”以用户视角查询”,优先用getByRole、getByLabelText这类语义查询,而不是按测试id或类名找节点。
// 一个可搜索下拉组件的单元测试
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import { SearchSelect } from './SearchSelect'
describe('SearchSelect', () => {
it('输入关键字后过滤选项并触发onChange', async () => {
const onChange = vi.fn()
const options = [
{ value: 'cn', label: '中国' },
{ value: 'us', label: '美国' },
{ value: 'jp', label: '日本' }
]
render( )
// 打开下拉
fireEvent.click(screen.getByRole('button', { name: /请选择/i }))
expect(screen.getByText('美国')).toBeInTheDocument()
// 输入过滤关键字
fireEvent.change(screen.getByPlaceholderText('搜索国家'), { target: { value: '美' } })
expect(screen.getByText('美国')).toBeInTheDocument()
expect(screen.queryByText('日本')).not.toBeInTheDocument()
// 选择选项触发回调
fireEvent.click(screen.getByText('美国'))
expect(onChange).toHaveBeenCalledWith('us')
})
it('空选项时显示空状态', () => {
render( )
expect(screen.getByText(/暂无匹配/i)).toBeInTheDocument()
})
})
异步场景用waitFor或findBy系列断言。props变化触发的更新,用rerender()重新渲染再断言。测试里不要过度依赖snapshot快照,组件样式微调会导致大面积快照漂移,真正有价值的是行为断言。
Vue组件测试:@vue/test-utils的挂载与事件验证
Vue组件测试用@vue/test-utils的mount挂载组件,trigger触发事件,wrapper.emitted()读取事件发射记录,与React的测试思路一致。
import { mount } from '@vue/test-utils'
import { describe, it, expect, vi } from 'vitest'
import Counter from './Counter.vue'
describe('Counter', () => {
it('点击按钮增加计数并发射事件', async () => {
const wrapper = mount(Counter, { props: { init: 5 } })
expect(wrapper.text()).toContain('5')
await wrapper.find('button.add').trigger('click')
expect(wrapper.text()).toContain('6')
expect(wrapper.emitted('change')[0]).toEqual([6])
})
it('渲染插槽内容', () => {
const wrapper = mount(Counter, {
slots: { default: '次数' }
})
expect(wrapper.find('.label').text()).toBe('次数')
})
})
Vue组件的props、emits、slots、expose都是测试入口,把组件当成独立单元验证。组合式API组件用mount({ setup() {…} })方式直接测逻辑函数,不用依赖父组件上下文。
端到端测试:Playwright在真实浏览器的验证
组件逻辑用Vitest覆盖,但组件之间的联动、路由跳转、接口调用、真实渲染效果,单元测试覆盖不到,需要端到端测试。Playwright的测试可以跨浏览器运行(chromium、firefox、webkit),支持自动等待、网络拦截和截图对比。
// 安装
npm init playwright@latest
// e2e/search-flow.spec.ts
import { test, expect } from '@playwright/test'
test('搜索到列表详情页完整流程', async ({ page }) => {
await page.goto('https://app.example.com/')
// 模拟网络请求(拦截真实接口)
await page.route('**/api/search**', route => {
route.fulfill({ json: [{ id: 1, name: '测试商品' }] })
})
await page.getByPlaceholder('输入商品名').fill('测试商品')
await page.getByRole('button', { name: '搜索' }).click()
await expect(page.getByText('测试商品')).toBeVisible()
await page.getByText('测试商品').click()
await expect(page).toHaveURL(/\/detail\/1/)
})
实际项目里接口全部mock容易失真,建议只在关键场景mock,多数场景跑真实接口或测试环境接口。Playwright的trace、screenshot、video在失败时自动保留,出问题直接看回放,排查速度比截图日志快很多。
测试分层与CI集成
三层测试的侧重点:单元测试测逻辑、端到端测流程、视觉回归测样式。CI配置上把单元测试放MR前快速跑,端到端测试放MR合并后跑完整回归,避免提交频繁时等待时间过长。
# .gitlab-ci.yml 测试阶段示例
test:unit:
script:
- npm ci
- npm run test -- --run
test:e2e:
script:
- npm ci
- npx playwright install --with-deps
- npm run build
- npx playwright test
artifacts:
when: always
paths:
- test-results/
- playwright-report/
expire_in: 7 days
覆盖率阈值建议组件库80%以上、业务代码60%以上,以核心逻辑和分支覆盖为准,追求数字而堆测试用例意义不大。测试写完之后能改出来的规律:先写断言再写实现(红-绿-重构),把组件拆到可以单独测的粒度,异步逻辑抽成纯函数直接测,这些做法比选哪套框架更重要。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/qian-duan-zu-jian-ce-shi-shi-zhan-vitest-dan-yuan-ce-shi-yu/