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

求助:Jest测试useDebounceCallback组件时假定时器问题

问题排查与修复方案

核心问题分析

你的测试失败主要源于三个关键错误:

  1. useDebounceCallback Mock 逻辑错误:当前Mock没有返回可调用的防抖函数,导致组件内的handleSearch是undefined,触发onChange时无法执行后续逻辑。
  2. useSearchParams 返回值类型错误:用Map模拟返回值,但实际useSearchParams返回的是URLSearchParams实例,会导致组件内参数处理异常。
  3. Router Mock 未完整初始化:缺少对useRouter返回对象的正确Mock,导致router.push的调用无法被追踪。

修复步骤与代码调整

1. 修正 useDebounceCallback 的 Mock

正确的防抖函数Mock需要返回一个可调用的函数,当该函数被触发时,会在延迟后执行传入的回调:

jest.mock('usehooks-ts', () => ({
  useDebounceCallback: jest.fn((fn) => {
    // 返回防抖函数,调用时携带参数执行原回调
    return (...args) => {
      setTimeout(() => fn(...args), 500);
    };
  }),
}));

2. 修正 useSearchParams 的 Mock

将返回值改为URLSearchParams实例,匹配真实场景:

useSearchParams.mockReturnValue(new URLSearchParams('search='));

3. 完善 Router 相关 Hooks 的 Mock

在测试文件顶部添加完整的Next.js Router Hooks Mock,并在beforeEach中初始化mock对象:

import { useRouter, usePathname, useSearchParams } from 'next/navigation';

jest.mock('next/navigation', () => ({
  useRouter: jest.fn(),
  usePathname: jest.fn(),
  useSearchParams: jest.fn(),
}));

const routerMock = { push: jest.fn() };

完整修复后的测试代码

import { render, fireEvent } from '@testing-library/react';
import { useRouter, usePathname, useSearchParams } from 'next/navigation';
import { ParamSearch } from './ParamSearch';

jest.mock('next/navigation', () => ({
  useRouter: jest.fn(),
  usePathname: jest.fn(),
  useSearchParams: jest.fn(),
}));

jest.mock('usehooks-ts', () => ({
  useDebounceCallback: jest.fn((fn) => {
    return (...args) => {
      setTimeout(() => fn(...args), 500);
    };
  }),
}));

describe('ParamSearch', () => {
  const routerMock = { push: jest.fn() };

  beforeEach(() => {
    useRouter.mockReturnValue(routerMock);
    jest.useFakeTimers();
    routerMock.push.mockClear(); // 重置调用记录
  });

  afterEach(() => {
    jest.runOnlyPendingTimers();
    jest.useRealTimers();
  });

  it('calls handleSearch as debounced callback after delay', () => {
    usePathname.mockReturnValue('/test-search');
    useSearchParams.mockReturnValue(new URLSearchParams('search='));
    
    const { getByPlaceholderText } = render(
      <ParamSearch placeholder={'Search'} defaultSearchValue={''} />
    );
    
    const searchInput = getByPlaceholderText('Search');
    fireEvent.change(searchInput, { target: { value: 'x' } });
    
    // 延迟前未触发push
    expect(routerMock.push).not.toHaveBeenCalled();
    
    // 快进定时器到防抖延迟时间
    jest.advanceTimersByTime(500);
    
    // 验证push被正确调用
    expect(routerMock.push).toHaveBeenCalledWith('/test-search?search=x');
  });
});

额外优化建议

组件内handleSearch的参数类型存在冗余,建议将event: '' | ChangeEvent<HTMLInputElement>改为event: ChangeEvent<HTMLInputElement>,因为onChange事件的参数始终是ChangeEvent类型,空字符串的类型定义属于错误冗余。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 20:37:13