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

Jest测试Redux-Saga时多次Mock封装GraphQL请求失效问题

问题

测试Redux-Saga时遇到Mock封装的GraphQL API请求失效的问题:

  • GraphQL请求通过axios封装在requestApiUtil中,API文件包含sampleOneApiRequest.js、sampleTwoApiRequest.js和导出入口index.js
  • 待测试的sampleSaga会根据payload中的switch值调用不同API
  • 用jest.mockMock API后,两个测试用例执行时第二个总会触发真实后端请求;调换顺序后仍是第二个失效,单独运行单个测试则正常

相关代码

API文件

/path/api/sampleOneApiRequest.js
import { requestApi } from 'utils';

export const sampleOneApiRequest = ({ arg1 }) => requestApi.query(`
  sampleData {
    id
  }
`)
/path/api/sampleTwoApiRequest.js
import { requestApi } from 'utils';

export const sampleTwoApiRequest = ({ arg1 }) => requestApi.query(`
  otherSampleData {
    id
  }
`)
/path/api/index.js
export * from './sampleOneApiRequest';
export * from './sampleTwoApiRequest';

Saga代码 /path/redux/saga/sampleSaga/sampleSaga.js

import { all, put } from 'redux-saga/effects';
import { successAction, failureAction } from 'path/redux/action';
import { sampleOneApiRequest, sampleTwoApiRequest } from 'path/api';

export function* sampleSaga({ payload }) {
  const switch = payload ? payload.switch : false;
  try {
    let response;
    if (switch) {
      response = (yield sampleOneApiRequest());
    } else {
      response = (yield sampleTwoApiRequest());
    }

    if (response.errors) {
      return yield put(failureAction(response.errors));
    }

    yield put(successAction(response.data));
  } catch (error) {
    yield put(failureAction(error));
  }
}

测试代码 /path/redux/saga/sampleSaga/sampleSaga.test.js

import { sampleSaga } from './sampleSaga';
import { sampleOneApiRequest, sampleTwoApiRequest } from 'path/api';
import { successAction } from 'path/redux/action';
import { put } from 'redux-saga/effects';

jest.mock('path/api', () => ({
  sampleOneApiRequest: async () => { id: 'sample1' },
  sampleTwoApiRequest: async () => { id: 'sample2' }
}));

describe('test sampleSaga', () => {
  
  test('test sampleSaga switch false', () => {
    const generator   = sampleSaga({ payload: { switch: false } });
    const apiResponse = { data: { id: 'sample1' } };
    
    expect(generator.next().value).toEqual(sampleOneApiRequest());
    expect(generator.next(apiResponse).value).toEqual(put(successAction(apiResponse)))
  });
  
  test('test sampleSaga switch true', () => {
    const generator   = sampleSaga({ payload: { switch: true } });
    const apiResponse = { data: { id: 'sample2' } };
    
    expect(generator.next().value).toEqual(sampleTwoApiRequest());
    expect(generator.next(apiResponse).value).toEqual(put(successAction(apiResponse)))
  });

});

原因分析与解决方案

核心原因

  1. Mock写法语法错误:当前Mock返回的async () => { id: 'sample1' }存在逻辑问题——大括号被解析为函数体而非对象字面量,实际返回undefined,导致Saga逻辑进入错误分支,触发真实请求。
  2. 模块缓存与Mock状态污染:Jest会缓存模块导入结果,第一次测试调用Mock函数后,Mock状态未重置,第二个测试用例拿到的Mock函数已不是初始状态,无法正确拦截请求。
  3. 断言逻辑错误:用toEqual(sampleOneApiRequest())断言时,每次调用都会生成新的Promise实例,两次Promise不相等会导致断言失败,Jest未正确拦截后续逻辑,间接触发真实请求。

修复步骤

1. 修正Mock函数写法

改用jest.fn()创建模拟函数,确保返回值正确:

jest.mock('path/api', () => ({
  sampleOneApiRequest: jest.fn().mockResolvedValue({ data: { id: 'sample1' } }),
  sampleTwoApiRequest: jest.fn().mockResolvedValue({ data: { id: 'sample2' } })
}));

2. 重置Mock状态

在每个测试用例前清空Mock调用记录,避免测试间污染:

describe('test sampleSaga', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });
  
  // 测试用例...
});

3. 修正断言逻辑

使用Redux-Saga的call effect来断言函数调用,而非直接对比Promise实例:

import { call, put } from 'redux-saga/effects';

// 测试用例中:
expect(generator.next().value).toEqual(call(sampleOneApiRequest));

4. 修复关键字冲突

switch是JavaScript关键字,不能用作变量名,改为合法名称:

export function* sampleSaga({ payload }) {
  const isSwitch = payload ? payload.switch : false;
  try {
    let response;
    if (isSwitch) {
      response = (yield call(sampleOneApiRequest));
    } else {
      response = (yield call(sampleTwoApiRequest));
    }
    // 后续逻辑...
  } catch (error) {
    yield put(failureAction(error));
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 19:17:19