为何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用法有关,求排查错误原因。
错误原因分析
- mock对象断言错误:你创建了
IsValueAlreadyRegisteredMock并让目标函数返回它,但Hook里只调用了IsValueAlreadyRegistered(email, "isEmailRegistered")(即高阶函数本身),并没有调用它返回的dispatch函数。你断言的是返回的mock函数,但实际应该断言被spy的IsValueAlreadyRegistered本身。 - 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
相关产品推荐
相关产品推荐

