如何使用React Testing Library编写测试用例并覆盖指定代码声明
一、React Testing Library 编写测试用例的基本方法
1. 安装必要依赖
首先确保项目中安装核心测试包:
npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event
如果涉及Redux、路由等依赖,还需安装对应测试工具(比如redux-mock-store或@reduxjs/toolkit/test-utils)。
2. 基础测试示例
以简单计数器组件为例:
组件代码(Counter.js):
import { useState } from 'react'; export default function Counter() { const [count, setCount] = useState(0); return ( <div> <p>当前计数: {count}</p> <button onClick={() => setCount(count + 1)}>加1</button> </div> ); }
对应的测试文件(Counter.test.js):
import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import Counter from './Counter'; // 测试组件初始渲染 test('渲染初始计数为0', () => { render(<Counter />); expect(screen.getByText('当前计数: 0')).toBeInTheDocument(); }); // 测试按钮交互 test('点击按钮后计数加1', async () => { const user = userEvent.setup(); render(<Counter />); const button = screen.getByRole('button', { name: /加1/i }); await user.click(button); expect(screen.getByText('当前计数: 1')).toBeInTheDocument(); });
3. 常见场景处理
- 异步组件测试:针对带数据请求的组件,用
findBy*查询(结合async/await)或waitFor断言异步更新:test('加载完成后显示用户列表', async () => { render(<UserList />); const userItem = await screen.findByText('张三'); expect(userItem).toBeInTheDocument(); }); - 条件渲染测试:通过修改props或上下文状态,验证不同分支的渲染结果:
test('未登录时显示登录按钮', () => { render(<Header isLoggedIn={false} />); expect(screen.getByRole('button', { name: /登录/i })).toBeInTheDocument(); expect(screen.queryByText('个人中心')).not.toBeInTheDocument(); });
二、覆盖
useSelector 代码的测试方案 针对代码:
const { UserData, UserSessionComplete } = useSelector((state) => state?.header);
要覆盖这段代码,核心是模拟Redux Store并传入不同state场景,确保组件能正确读取state.header字段,同时处理state.header为undefined的边界情况。
1. 封装带Redux的渲染函数
先创建自定义渲染工具,将组件包裹在Redux的Provider中,方便复用:
import { render } from '@testing-library/react'; import { Provider } from 'react-redux'; import { configureStore } from '@reduxjs/toolkit'; import rootReducer from '../reducers'; // 你的根reducer export function renderWithProviders( ui, { preloadedState = {}, store = configureStore({ reducer: rootReducer, preloadedState }), ...renderOptions } = {} ) { function Wrapper({ children }) { return <Provider store={store}>{children}</Provider>; } return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) }; }
2. 测试正常场景(state.header存在)
验证组件能正确读取UserData和UserSessionComplete:
test('读取Redux中的用户数据和会话状态', () => { const preloadedState = { header: { UserData: { id: 1, name: 'Indushree' }, UserSessionComplete: true } }; renderWithProviders(<YourComponent />, { preloadedState }); // 根据组件使用状态的方式断言,比如显示用户名或会话状态 expect(screen.getByText('Indushree')).toBeInTheDocument(); expect(screen.getByText('会话已完成')).toBeInTheDocument(); });
3. 测试边界场景(state.header为undefined)
验证可选链state?.header的容错处理,确保组件不崩溃:
test('当state.header不存在时组件正常渲染', () => { const preloadedState = { // 不设置header字段 }; // 断言组件无报错 expect(() => renderWithProviders(<YourComponent />, { preloadedState })).not.toThrow(); // 如果组件有默认提示,可额外断言 expect(screen.getByText('未获取到用户信息')).toBeInTheDocument(); });
4. 测试部分字段缺失的场景
验证组件在state.header存在但字段不全时的处理逻辑:
test('当UserData缺失时组件正常处理', () => { const preloadedState = { header: { UserSessionComplete: false // 不设置UserData } }; renderWithProviders(<YourComponent />, { preloadedState }); expect(screen.getByText('用户数据未加载')).toBeInTheDocument(); });
内容的提问来源于stack exchange,提问作者Indushree N
相关产品推荐
相关产品推荐

