React 组件测试实战:用 Vitest 与 React Testing Library 把交互固化成用例
test12026-10-151 次阅读
为什么组件要写测试
组件写完能跑起来,不等于改了不会出事。React 组件把状态、事件和 DOM 绑在一起,单元测试能把这些交互固化成可回归的用例。Vitest 启动快、和 Vite 配置复用,配合 React Testing Library(RTL)按"用户视角"查 DOM,比直接断言组件内部 state 更稳。
配置最小环境
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: './src/test/setup.ts',
},
})
setup 里引入 @testing-library/jest-dom 的 matcher,让 toBeInTheDocument 这类断言可用:
// src/test/setup.ts
import '@testing-library/jest-dom'
给一个计数器组件写测试
// Counter.tsx
import { useState } from 'react'
export function Counter() {
const [n, setN] = useState(0)
return (
<div>
<span data-testid="value">{n}</span>
<button onClick={() => setN(n + 1)}>+1</button>
</div>
)
}
// Counter.test.tsx
import { render, screen, fireEvent } from '@testing-library/react'
import { describe, it, expect } from 'vitest'
import { Counter } from './Counter'
describe('Counter', () => {
it('点击按钮让数值加一', () => {
render(<Counter />)
const value = screen.getByTestId('value')
expect(value).toHaveTextContent('0')
fireEvent.click(screen.getByText('+1'))
expect(value).toHaveTextContent('1')
})
})
异步流程怎么测
组件里发请求再渲染结果,用 findBy 系列等待异步节点,RTL 内部按轮询查询而非 sleep:
it('加载后展示用户名', async () => {
render(<UserProfile id={1} />)
expect(await screen.findByText('alice')).toBeInTheDocument()
})
几个容易踩的坑
- 用 getByText、getByRole 而非查 className,断言才不随样式重构而崩。
- 每个 it 之间 RTL 自动清理 DOM(globals 开启时),无需手动 unmount。
- 别断言私有 state,只测用户能观察到的行为和输出。
T
test1
文章作者
为什么组件要写测试 组件写完能跑起来,不等于改了不会出事。React 组件把状态、事件和 DOM 绑在一起,单元测试能...
- 分类
- 技术
- 发布时间
- 2026-10-15
- 字数
- 约 1638 字
- 阅读
- 1 次
