如何正确Mock useWindowSize Hook以通过测试并提升分支覆盖率?
问题
我尝试Mock自定义Hook useWindowSize,以覆盖组件中基于窗口宽度的分支逻辑,但测试时报错:
GamesEngagement Component › width is less than 425 Cannot spy the useWindowSize property because it is not a function; undefined given instead 102 | 103 | it('width is less than 425', () => { > 104 | jest.spyOn(hooks, 'useWindowSize').mockImplementation(() => ({width: 400, height: 400})) | ^ 105 | 106 | render(<GamesEngagement />); 107 | expect(screen.getByTestId('mocked-charts')).toBeInTheDocument();
相关代码
测试文件片段:
import React from 'react'; import { render, screen, fireEvent, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import '@testing-library/jest-dom/extend-expect'; import GamesEngagement from 'pages/Analytics/GamesEngagement'; import { useGetEngagementInsightsQuery } from 'app/services/analytics'; import localStorage from 'utils/localStorage'; import hooks from 'utils/hooks'; // ...其他mock代码 describe(`GamesEngagement Component`, () => { beforeEach(() => { useGetEngagementInsightsQuery.mockReturnValue({ data: [{ mark: 'A', engagementRate: 80 }], isFetching: true }); }); it(`rendered correctly`, () => { render(<GamesEngagement />); expect(screen.getByText('Games engagement rate')).toBeInTheDocument(); }); it('width is less than 425', () => { jest.spyOn(hooks, 'useWindowSize').mockImplementation(() => ({width: 400, height: 400})) render(<GamesEngagement />); expect(screen.getByTestId('mocked-charts')).toBeInTheDocument(); }) });
组件中使用useWindowSize的方式:
const { width } = useWindowSize(); // ... <Charts type='line' labels={width < 425 ? labels.slice(-5) : labels} datasets={[ { data: width < 425 ? data.slice(-5) : data, backgroundColor: '#E1DCFF' } ]} // ...
useWindowSize的实现:
function useWindowSize() { const [windowSize, setWindowSize] = useState({ width: undefined, height: undefined }); useEffect(() => { function handleResize() { setWindowSize({ width: window.innerWidth, height: window.innerHeight }); } window.addEventListener('resize', handleResize); handleResize(); return () => window.removeEventListener('resize', handleResize); }, []); return windowSize; } export default useWindowSize;
解决方案
报错核心原因是未正确Mockutils/hooks模块,导致hooks.useWindowSize为undefined,按以下步骤修复:
1. 完整Mockutils/hooks模块
取消注释之前的Mock代码,将useWindowSize定义为jest mock函数,方便后续动态修改返回值:
jest.mock('utils/hooks', () => ({ __esModule: true, useWindowSize: jest.fn() }));
2. 在测试用例中直接设置Mock返回值
无需使用jest.spyOn,直接给已Mock的useWindowSize函数指定返回值:
it('should use sliced data when width is less than 425', () => { hooks.useWindowSize.mockReturnValue({ width: 400, height: 400 }); render(<GamesEngagement />); expect(screen.getByTestId('mocked-charts')).toBeInTheDocument(); });
3. 补充测试覆盖另一分支
为提升分支覆盖率,添加窗口宽度≥425的测试用例:
it('should use full data when width is greater than or equal to 425', () => { hooks.useWindowSize.mockReturnValue({ width: 500, height: 600 }); render(<GamesEngagement />); expect(screen.getByTestId('mocked-charts')).toBeInTheDocument(); });
修改后的完整测试片段
import React from 'react'; import { render, screen, fireEvent, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import '@testing-library/jest-dom/extend-expect'; import GamesEngagement from 'pages/Analytics/GamesEngagement'; import { useGetEngagementInsightsQuery } from 'app/services/analytics'; import localStorage from 'utils/localStorage'; import hooks from 'utils/hooks'; jest.mock('components/Charts', () => { return { __esModule: true, default: () => { return <div data-testid='mocked-charts'></div>; } }; }); jest.mock('app/services/analytics', () => ({ useGetEngagementInsightsQuery: jest.fn() })); jest.mock('utils/localStorage', () => ({ getPlatform: jest.fn(), getToken: jest.fn() })); // 正确Mock utils/hooks模块 jest.mock('utils/hooks', () => ({ __esModule: true, useWindowSize: jest.fn() })); // ...其他describe块 describe(`GamesEngagement Component - Window Size Tests`, () => { beforeEach(() => { useGetEngagementInsightsQuery.mockReturnValue({ data: [{ mark: 'A', engagementRate: 80 }], isFetching: true }); }); it(`rendered correctly`, () => { hooks.useWindowSize.mockReturnValue({ width: 500, height: 600 }); render(<GamesEngagement />); expect(screen.getByText('Games engagement rate')).toBeInTheDocument(); }); it('should use sliced data when width is less than 425', () => { hooks.useWindowSize.mockReturnValue({ width: 400, height: 400 }); render(<GamesEngagement />); expect(screen.getByTestId('mocked-charts')).toBeInTheDocument(); }); it('should use full data when width is greater than or equal to 425', () => { hooks.useWindowSize.mockReturnValue({ width: 500, height: 600 }); render(<GamesEngagement />); expect(screen.getByTestId('mocked-charts')).toBeInTheDocument(); }); });
内容的提问来源于stack exchange,提问作者Jack Sparrow
相关产品推荐
相关产品推荐

