如何使用Jest测试更新state并传递给子组件的useEffect以提升代码覆盖率
解决方案
核心思路
React Testing Library 推崇测试用户可见的行为与输出,不需要直接调用或测试组件内部的getAlertsHeader、useEffect逻辑,只需要模拟Redux状态后,校验Header组件渲染的内容是否符合预期即可覆盖所有未覆盖的代码行,全程不需要依赖Enzyme。
完整测试代码
如果测试环境没有自动引入act,需要提前从@testing-library/react或react-dom/test-utils引入。如果你的renderWithStore目前不返回store实例,可以先修改其实现,在返回值中加上生成的Redux store,方便测试时调用dispatch更新状态。
import { screen, waitFor, act } from '@testing-library/react'; // 替换为你实际的Redux action、组件引入路径 import { setSavedSearches } from '你的action文件路径'; import Alerts from './Alerts'; describe('Alerts', () => { // 基础渲染用例,同时覆盖useEffect无searchStats的初始分支 test('it should render without error', () => { const { container } = renderWithStore(<Alerts />); expect(container).toBeDefined(); expect(screen.getByText('Alerts Page')).toBeInTheDocument(); }); // 覆盖有searchStats、带location的场景,命中getAlertsHeader自定义副标题分支 test('it should render header correctly with location in search stats', async () => { const { store } = renderWithStore(<Alerts />); // 构造模拟的search数据 const mockSearchStats = { totalSaved: 5, indexes: [{ id: 'index1' }, { id: 'index2' }], location: '北京' }; // 更新Redux状态触发useEffect执行 act(() => { store.dispatch(setSavedSearches({ data: { searchStats: mockSearchStats } })); }); // 等待异步状态更新完成,校验Header渲染的内容符合预期 await waitFor(() => { // 根据Header组件的实际渲染逻辑查询对应文本即可 expect(screen.getByText('Searches (5)')).toBeInTheDocument(); expect(screen.getByText('Searches For 北京')).toBeInTheDocument(); // 如果需要校验searchIndexes,可给Header对应渲染节点加data-testid="search-indexes",再校验属性或文本 }); }); // 覆盖有searchStats、无location的场景,命中getAlertsHeader默认副标题分支 test('it should render header with default subtitle when no location exists', async () => { const { store } = renderWithStore(<Alerts />); const mockSearchStats = { totalSaved: 3, indexes: [{ id: 'index3' }], location: null }; act(() => { store.dispatch(setSavedSearches({ data: { searchStats: mockSearchStats } })); }); await waitFor(() => { expect(screen.getByText('Searches (3)')).toBeInTheDocument(); expect(screen.getByText('Organise your searches.')).toBeInTheDocument(); }); }); // 可选用例:覆盖useEffect卸载时isMounted设为false的清理分支 test('it should not update state after unmount', async () => { const { unmount, store } = renderWithStore(<Alerts />); // 先卸载组件 unmount(); // 此时再更新状态不会触发state更新,无报错即说明清理逻辑生效 act(() => { store.dispatch(setSavedSearches({ data: { searchStats: { totalSaved: 2, indexes: [], location: null } } })); }); expect(screen.queryByText('Searches (2)')).not.toBeInTheDocument(); }); });
覆盖率说明
上述用例执行后可以覆盖所有未覆盖的代码行:
useEffect的全部分支:无searchStats的初始分支、有searchStats的逻辑分支、组件卸载清理逻辑getAlertsHeader的全部分支:有location的自定义副标题分支、无location的默认副标题分支、indexes映射逻辑
如果你不方便直接查询Header渲染的文本,也可以给Header组件添加data-testid测试属性,通过属性值校验传入的props是否正确即可。
内容的提问来源于stack exchange,提问作者mcclosa
相关产品推荐
相关产品推荐

