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))) }); });
原因分析与解决方案
核心原因
- Mock写法语法错误:当前Mock返回的
async () => { id: 'sample1' }存在逻辑问题——大括号被解析为函数体而非对象字面量,实际返回undefined,导致Saga逻辑进入错误分支,触发真实请求。 - 模块缓存与Mock状态污染:Jest会缓存模块导入结果,第一次测试调用Mock函数后,Mock状态未重置,第二个测试用例拿到的Mock函数已不是初始状态,无法正确拦截请求。
- 断言逻辑错误:用
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
相关产品推荐
相关产品推荐

