You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 16:25:56