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

为何Jest无法监视IsValueAlreadyRegistered函数?

自定义Hook测试问题:Jest未检测到函数调用

我写了一个简单的自定义Hook,用来处理忘记密码流程:

import { IsValueAlreadyRegistered } from "../../entities/registration/actions";

export const useForgetPassword = () => {
  const activeStep = 0;
  const email = "any@any.com";

  const nextStep = (): void => {
    let isThereError: boolean = false;

    switch (activeStep) {
      case 0:
        if (!isThereError) {
          IsValueAlreadyRegistered(email, "isEmailRegistered");
        }
        break;
    }
  };

  return { nextStep };
};

这个Hook依赖了IsValueAlreadyRegistered函数,我想写测试确认它被传入预期参数调用,测试代码如下:

import { renderHook } from "@testing-library/react";
import { useForgetPassword } from ".";
import * as Registered from "../../entities/registration/actions";

describe("useForgetPassword", () => {
  const { result } = renderHook(() => useForgetPassword());

  const IsValueAlreadyRegisteredMock = jest.fn();

  test("nextStep", () => {
    jest
      .spyOn(Registered, "IsValueAlreadyRegistered")
      .mockReturnValue(IsValueAlreadyRegisteredMock);

    const { nextStep } = result.current;
    nextStep();

    expect(IsValueAlreadyRegisteredMock).toBeCalledWith(
      "any@any.com",
      "isEmailRegistered"
    );
  });
});

被调用的IsValueAlreadyRegistered是一个Redux thunk高阶函数:

import { registrationFormSlice } from ".";
import { AppDispatch } from "../store";

const { setEmail } = registrationFormSlice.actions;

export const IsValueAlreadyRegistered =
  (value: string, process: string) => async (dispatch: AppDispatch) => {
    switch (process) {
      case "isEmailRegistered":
        dispatch(setEmail({ value, error: false, text: "" }));
        break;
    }
  };

问题:我确认代码执行时IsValueAlreadyRegistered确实被调用且参数正确,但Jest始终提示该函数未被调用。怀疑和函数中的AppDispatch或者jest.spyOn的mockReturnValue用法有关,求排查错误原因。


错误原因分析

  1. mock对象断言错误:你创建了IsValueAlreadyRegisteredMock并让目标函数返回它,但Hook里只调用了IsValueAlreadyRegistered(email, "isEmailRegistered")(即高阶函数本身),并没有调用它返回的dispatch函数。你断言的是返回的mock函数,但实际应该断言被spy的IsValueAlreadyRegistered本身。
  2. mock时机错误:你在describe块中提前渲染了Hook,此时模块已经导入并绑定了原始的IsValueAlreadyRegistered函数引用,之后再mock已经无法覆盖这个引用,导致mock不生效。

修复后的测试代码

import { renderHook } from "@testing-library/react";
import { useForgetPassword } from ".";
import * as Registered from "../../entities/registration/actions";

describe("useForgetPassword", () => {
  test("nextStep calls IsValueAlreadyRegistered with correct parameters", () => {
    // 先mock目标函数,再渲染Hook
    const isRegisteredSpy = jest.spyOn(Registered, "IsValueAlreadyRegistered")
      // 高阶函数需要返回一个可执行的函数,这里mock一个空函数即可
      .mockReturnValue(jest.fn());

    // 在mock之后渲染Hook,确保Hook使用mock后的函数
    const { result } = renderHook(() => useForgetPassword());
    const { nextStep } = result.current;
    
    nextStep();

    // 断言被spy的原始函数被调用的参数
    expect(isRegisteredSpy).toHaveBeenCalledWith("any@any.com", "isEmailRegistered");
  });
});

额外说明

如果后续需要测试IsValueAlreadyRegistered返回的异步dispatch函数逻辑,你需要:

  • 在Hook中实际调用返回的函数,比如await IsValueAlreadyRegistered(email, "isEmailRegistered")(dispatch)
  • 或者在测试中手动获取高阶函数返回的函数并调用,再断言dispatch的行为

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 14:57:33