如何修复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
相关产品推荐
相关产品推荐

