You need to enable JavaScript to run this app. 如何在测试中仅监听指定的React Hook useState(如setMovies)
优惠活动
大模型
产品
解决方案
定价
更多

如何在测试中仅监听指定的React Hook useState(如setMovies)

解决思路

你现在的实现给所有useState返回了同一个setter mock,所以所有状态更新都会触发相同的日志。要隔离监听setMovies,只需要在mock useState的时候,给目标useState返回专属的监听setter,其他useState走普通逻辑即可。


方案1:通过初始值匹配(最稳妥,无需修改业务代码)

如果提前知道setMovies对应的状态初始值(比如电影列表初始值是空数组[]),直接在mock实现里判断传入的初始值,匹配到就返回带监听的setter,其余useState返回原生实现或者普通mock:

import React, { Dispatch, SetStateAction } from 'react';

// 入参传入目标useState的初始值,用来匹配
const createMockUseState = <T extends {}>(targetInitialValue: T) => {
  type TSetState = Dispatch<SetStateAction<T>>;
  // 专属监听setter,只给setMovies用
  const targetSetState: TSetState = jest.fn((prop) => {
    console.log('jest - spy setMovies 调用 = ', prop); 
  });
  // 其他useState用的普通setter
  const defaultSetState: TSetState = jest.fn();

  const spyUseState = jest.spyOn(React, 'useState') as jest.SpyInstance; 
  spyUseState.mockImplementation((initialValue) => {
    // 匹配到目标初始值,返回监听setter
    if (initialValue === targetInitialValue) {
      return [initialValue, targetSetState];
    }
    // 其余走普通mock,也可以换成 jest.requireActual('react').useState 走原生实现
    return [initialValue, defaultSetState];
  });
  
  // 把targetSetState暴露出去,方便后续断言调用次数/参数
  return { targetSetState };
};

interface Props {
  propertyToTest: boolean
};

describe('Search Movies', () => {
  describe('Onload - do first search()', () => {
    beforeAll(async () => {
      // 传入setMovies对应的初始值,比如是空数组就传[]
      const { targetSetState } = createMockUseState([]); 
      wrapper = mount(
          <ProviderMovies>
            <SearchMovies />
          </ProviderMovies>
      );  
      await new Promise((resolve) => setImmediate(resolve));
      await act(
       () =>
        new Promise<void>((resolve) => {
          resolve();
        })
      );
      
      // 后续可以直接断言
      expect(targetSetState).toHaveBeenCalledTimes(1);
    });
  });
});

方案2:通过调用顺序匹配

如果多个useState初始值相同,你可以提前数好setMovies对应的useState是组件渲染时第几次调用的,按次数匹配即可:

const createMockUseState = <T extends {}>(targetCallIndex: number) => {
  type TSetState = Dispatch<SetStateAction<T>>;
  const targetSetState: TSetState = jest.fn((prop) => {
    console.log('jest - spy setMovies 调用 = ', prop); 
  });
  const defaultSetState: TSetState = jest.fn();
  // 计数useState调用次数
  let callCount = 0;

  const spyUseState = jest.spyOn(React, 'useState') as jest.SpyInstance; 
  spyUseState.mockImplementation((initialValue) => {
    callCount++;
    // 调用次数匹配目标就返回监听setter
    if (callCount === targetCallIndex) {
      return [initialValue, targetSetState];
    }
    return [initialValue, defaultSetState];
  });
  
  return { targetSetState };
};

// 使用的时候传入setMovies是第几次调用的useState,比如是第2次就传2
const { targetSetState } = createMockUseState(2);

注意事项

  • 方案2依赖组件内useState的调用顺序,后续如果修改了组件内useState的顺序,需要同步更新测试里的序号。
  • 如果不需要mock其他useState的逻辑,可以把普通返回的部分换成原生useState实现,性能更好:
const originalUseState = jest.requireActual('react').useState;
// 不匹配的情况直接返回原生useState
return originalUseState(initialValue);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 11:06:04