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

Jest与redux-saga-test-plan中如何mock saga生成器函数

问题根因

你写的jest.spyOn不生效的核心原因:

  • chooseCorrectGenerator 内部直接调用了同模块的behaviour1,引用的是模块内的原始函数,不是模块导出对象上的属性
  • redux-saga-test-plan运行saga时会直接迭代生成器执行,不会走模块导出的引用路径,所以你挂载在导出对象上的spy完全不会被触发
方案1:正确mock子saga

如果确实需要mockbehaviour1的实现,不要用jest.spyOn,直接用redux-saga-test-plan自带的provide拦截器,它会在saga运行时拦截所有saga调用、副作用触发,不需要修改模块引用:

import * as sagaFunctions from './saga';
import { expectSaga } from 'redux-saga-test-plan';

test('flag为true时调用behaviour1', async () => {
  // 定义mock实现,不需要执行原saga逻辑
  const mockBehaviour1 = jest.fn(function* () {});

  await expectSaga(sagaFunctions.chooseCorrectGenerator, {
    type: "TEST_ACTION",
    payload: { flag: true }
  })
  .provide({
    // 拦截所有saga委托/调用
    saga(effect, next) {
      if (effect.fn === sagaFunctions.behaviour1) {
        mockBehaviour1(effect.args);
        return;
      }
      return next();
    },
    // 如果业务代码里有select、put、call等副作用,也可以在这里统一mock返回值
    select() {
      return {}; // 按业务需要返回mock的store数据
    }
  })
  .run();

  expect(mockBehaviour1).toBeCalledTimes(1);
});
方案2:更推荐的无mock测试方式

你要测试的核心是chooseCorrectGenerator的分支路由逻辑,完全不需要mockbehaviour1,两种写法更简单健壮:

写法1:静态步进测试(优先选)

用testSaga做静态单元测试,只会逐行校验当前saga产出的effect,不会真的执行子saga逻辑,运行速度极快,也不需要任何mock:

import { testSaga } from 'redux-saga-test-plan';
import * as sagaFunctions from './saga';

test('flag为true时委托behaviour1', () => {
  const testAction = {
    type: "TEST_ACTION",
    payload: { flag: true }
  };

  testSaga(sagaFunctions.chooseCorrectGenerator, testAction)
    .next()
    // 断言当前saga产出了调用behaviour1的effect,参数正确
    .call(sagaFunctions.behaviour1, testAction)
    .next()
    .isDone();
});

test('flag为false时委托behaviour2', () => {
  const testAction = {
    type: "TEST_ACTION",
    payload: { flag: false }
  };

  testSaga(sagaFunctions.chooseCorrectGenerator, testAction)
    .next()
    .call(sagaFunctions.behaviour2, testAction)
    .next()
    .isDone();
});

这种写法完全符合你的场景:你已经给behaviour1/behaviour2写了独立测试,当前测试只需要验证路由逻辑正确即可,不需要关心子saga内部实现。

写法2:集成式断言

如果需要做更贴近真实运行的集成测试,不需要mock子saga,只需要给子saga用到的select等副作用提供mock数据,断言最终触发的put等结果即可:

test('flag为true时走behaviour1逻辑流', async () => {
  await expectSaga(sagaFunctions.chooseCorrectGenerator, {
    type: "TEST_ACTION",
    payload: { flag: true }
  })
  .provide({
    // 只mock全局副作用,不mock子saga
    select() {
      return { /* mock业务需要的store状态 */ };
    }
  })
  // 断言behaviour1逻辑最终会触发的action
  // .put({ type: 'BEHAVIOUR1_SUCCESS' })
  // 反向断言不会触发behaviour2的结果
  .not.put({ type: 'BEHAVIOUR2_SUCCESS' })
  .run();
});
注意事项
  • 不要用jest.spyOn、jest.mock去mock同模块内的saga函数,这类mock无法拦截模块内部的函数调用,只会导致测试逻辑脆弱
  • 路由类saga优先用静态步进测试,逻辑清晰、运行快、不会被子saga的内部变动影响
  • 只有当需要验证跨saga的完整逻辑流时,才用集成式的expectSaga写法

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 21:51:19