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

如何使用Jest和Enzyme模拟返回滚动元素数据的自定义Hook

如何用Jest和Enzyme模拟自定义useOnScroll Hook?

针对你的场景,有三种常见测试方式,分别对应不同的测试需求:

一、测试组件时Mock Hook返回值

如果只需验证组件根据Hook返回的scrollTop渲染正确类名,无需测试Hook内部逻辑,可直接用Jest mock整个Hook:

import React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './MyComponent';
import useOnScroll from '../utils/useOnScroll';

// Mock自定义Hook
jest.mock('../utils/useOnScroll', () => jest.fn());

describe('MyComponent', () => {
  it('滚动容器scrollTop>0时,header添加scrolled类', () => {
    // 指定Hook返回的scrollTop为100
    useOnScroll.mockReturnValue({ scrollTop: 100, direction: 'down' });
    
    const wrapper = shallow(<MyComponent />);
    const header = wrapper.find('[data-test="btmSheet-header"]');
    
    expect(header.hasClass('scrolled')).toBe(true);
  });

  it('滚动容器scrollTop=0时,header不添加scrolled类', () => {
    useOnScroll.mockReturnValue({ scrollTop: 0, direction: 'up' });
    
    const wrapper = shallow(<MyComponent />);
    const header = wrapper.find('[data-test="btmSheet-header"]');
    
    expect(header.hasClass('scrolled')).toBe(false);
  });
});

二、模拟真实滚动行为测试组件与Hook的交互

如果需要验证真实滚动触发Hook逻辑后组件的变化,不mock Hook,直接模拟DOM滚动事件:

import React from 'react';
import { mount } from 'enzyme';
import MyComponent from './MyComponent';

describe('MyComponent滚动行为', () => {
  it('容器向下滚动时,header添加scrolled类', () => {
    const wrapper = mount(<MyComponent />);
    const scrollContainer = wrapper.find('[data-test="btmSheet-body"]').getDOMNode();
    
    // 设置scrollTop并触发scroll事件
    scrollContainer.scrollTop = 50;
    scrollContainer.dispatchEvent(new Event('scroll'));
    
    // 强制更新组件,确保Hook状态更新同步
    wrapper.update();
    
    const header = wrapper.find('[data-test="btmSheet-header"]');
    expect(header.hasClass('scrolled')).toBe(true);
  });

  it('容器scrollTop重置为0时,header移除scrolled类', () => {
    const wrapper = mount(<MyComponent />);
    const scrollContainer = wrapper.find('[data-test="btmSheet-body"]').getDOMNode();
    
    // 先滚动到50
    scrollContainer.scrollTop = 50;
    scrollContainer.dispatchEvent(new Event('scroll'));
    wrapper.update();
    
    // 再重置scrollTop为0
    scrollContainer.scrollTop = 0;
    scrollContainer.dispatchEvent(new Event('scroll'));
    wrapper.update();
    
    const header = wrapper.find('[data-test="btmSheet-header"]');
    expect(header.hasClass('scrolled')).toBe(false);
  });
});

三、单独测试useOnScroll Hook的逻辑

如果需要验证Hook自身的逻辑(比如滚动方向判断、事件监听管理),可以用@testing-library/react的renderHook工具:

import { renderHook, act } from '@testing-library/react';
import useOnScroll from './useOnScroll';

describe('useOnScroll Hook', () => {
  it('向下滚动时返回正确的scrollTop和direction', () => {
    // 创建模拟DOM元素
    const mockEl = document.createElement('div');
    const elRef = { current: mockEl };
    
    const { result } = renderHook(() => useOnScroll(elRef));
    
    // 模拟滚动事件
    act(() => {
      mockEl.scrollTop = 100;
      mockEl.dispatchEvent(new Event('scroll'));
    });
    
    expect(result.current.scrollTop).toBe(100);
    expect(result.current.direction).toBe('down');
  });

  it('向上滚动时返回direction为up', () => {
    const mockEl = document.createElement('div');
    const elRef = { current: mockEl };
    
    const { result } = renderHook(() => useOnScroll(elRef));
    
    // 先向下滚动到100
    act(() => {
      mockEl.scrollTop = 100;
      mockEl.dispatchEvent(new Event('scroll'));
    });
    
    // 再向上滚动到50
    act(() => {
      mockEl.scrollTop = 50;
      mockEl.dispatchEvent(new Event('scroll'));
    });
    
    expect(result.current.scrollTop).toBe(50);
    expect(result.current.direction).toBe('up');
  });

  it('正确添加和移除scroll事件监听', () => {
    const mockEl = document.createElement('div');
    const addSpy = jest.spyOn(mockEl, 'addEventListener');
    const removeSpy = jest.spyOn(mockEl, 'removeEventListener');
    
    const elRef = { current: mockEl };
    
    const { unmount } = renderHook(() => useOnScroll(elRef));
    
    // 验证添加了监听
    expect(addSpy).toHaveBeenCalledWith('scroll', expect.any(Function));
    
    // 卸载Hook,验证移除了监听
    unmount();
    expect(removeSpy).toHaveBeenCalledWith('scroll', expect.any(Function));
  });
});

注意事项

  • 测试真实滚动行为时,必须用mount而非shallow,因为需要获取真实DOM元素触发事件。
  • 触发滚动事件后,调用wrapper.update()确保组件状态同步更新。
  • 测试Hook时,用act()包裹事件触发逻辑,确保React状态更新完成后再断言。

内容的提问来源于stack exchange,提问作者Sushmit Sagar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 00:55:13