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

如何修复Redux-Saga中API调用测试时Mock函数未被调用的问题

修复Redux-Saga测试中Mock函数调用断言失败的问题

咱们先搞清楚为啥你的测试会失败:手动遍历Generator的测试方式,根本不会实际执行call effect对应的函数。当你调用gen.next()产出call(api.getStuff)这个effect对象时,这只是一个描述「我要调用哪个函数」的普通对象而已——真正去执行这个函数是Redux-Saga中间件的活儿,手动遍历Generator只是在检查saga的流程步骤对不对,不会触发函数调用。所以你的getStuffMock从头到尾都没被调用过,断言自然就失败了。


修复方案一:调整断言逻辑,验证Effect的正确性

既然手动遍历是为了检查saga产出的effect是否符合预期,那咱们换个思路:不要断言函数被调用,而是断言产出的call effect是不是指向了正确的api.getStuff函数。修改后的测试代码如下:

import * as api from "../api";
import { call, put } from "redux-saga/effects";
const getStuffSpy = jest.spyOn(api, "getStuff");

describe("search saga", () => {
  let gen: Generator, response: any;

  beforeEach(() => {
    getStuffSpy.mockClear();
    gen = getThemEffect();
    // 这里直接给spy mock一个默认返回值就行,不用单独定义getStuffMock
    getStuffSpy.mockResolvedValue({ data: { data: [] } });
  });

  describe("server success response", () => {
    beforeEach(() => {
      response = { data: { data: ["1", "2", "3"] } };
    });

    it("should create correct success flow", () => {
      // 第一步:验证saga先发出了request action
      expect(gen.next()).toEqual({ value: put(requestAction()), done: false });
      
      // 第二步:验证产出的call effect确实指向了api.getStuff
      const callEffect = gen.next().value;
      expect(callEffect).toEqual(call(api.getStuff));
      
      // 第三步:传入mock的响应,验证发出success action
      expect(gen.next(response)).toEqual({ value: put(successAction(["1", "2", "3"])), done: false });
      
      // 第四步:验证saga正常结束
      expect(gen.next()).toEqual({ value: undefined, done: true });
    });
  });
});

修复方案二:用runSaga实际运行Saga,验证函数调用

如果你确实需要验证getStuff函数真的被调用了,那得用Redux-Saga提供的runSaga工具来实际运行saga,让中间件去处理effect并执行函数。这种方式更贴近真实的运行环境,代码如下:

import * as api from "../api";
import { runSaga } from "redux-saga";
import { getThemEffect } from "./your-saga-file"; // 替换成你的saga文件路径
import { requestAction, successAction, failureAction } from "./your-actions-file"; // 替换成你的action文件路径

const getStuffSpy = jest.spyOn(api, "getStuff");

describe("search saga", () => {
  beforeEach(() => {
    getStuffSpy.mockClear();
  });

  describe("server success response", () => {
    it("should call getStuff and dispatch success action", async () => {
      // 给API函数mock一个返回值
      const mockResponse = { data: { data: ["1", "2", "3"] } };
      getStuffSpy.mockResolvedValue(mockResponse);

      // 用来捕获saga dispatch的所有action
      const dispatchedActions: any[] = [];
      const mockDispatch = (action: any) => dispatchedActions.push(action);

      // 运行saga,等待它执行完成
      await runSaga(
        { dispatch: mockDispatch },
        getThemEffect
      ).toPromise();

      // 现在就可以断言API函数被调用了
      expect(getStuffSpy).toHaveBeenCalled();
      
      // 同时也能验证dispatch的action顺序是否正确
      expect(dispatchedActions).toEqual([
        requestAction(),
        successAction(["1", "2", "3"])
      ]);
    });
  });
});

小提示

  • 手动遍历Generator的方式,适合快速验证saga的流程步骤(比如先发request,再调API,最后发success),但不会触发实际的副作用。
  • 用runSaga的方式,更适合测试副作用的实际行为(比如API有没有被调用、action有没有正确分发),测试结果更贴近真实场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 16:38:10