如何用Jest和React Testing Library测试含GraphQL请求的Node.js函数并解决fetch问题
解决方案
1. 解决 ReferenceError: fetch is not defined 问题
Node.js 环境本身不支持浏览器的 fetch API,你有两种解决方式:
方法一:引入 polyfill
安装 whatwg-fetch:
npm install whatwg-fetch --save-dev
在测试文件顶部或 Jest 全局配置文件中引入:
import 'whatwg-fetch';
方法二:直接 mock fetch(单元测试推荐)
在测试文件中手动 mock 全局 fetch,无需额外依赖:
global.fetch = jest.fn();
2. getData 函数的单元测试
通过 mock fetch 模拟 GraphQL 请求的成功、失败场景,专注测试函数逻辑:
import getData from './getData'; import GET_DATA from './getData.gql'; // 全局 mock fetch global.fetch = jest.fn(); describe('getData', () => { const mockTargetUrl = '/foo/test'; const mockSuccessResponse = { data: { page: { content: { type: 'MyPage', id: 'e523f6f6-6e73-4bb0-8737-95b63e1f9e6c' } } } }; beforeEach(() => { jest.clearAllMocks(); // 清除测试间的 mock 调用记录 process.env.ENDPOINT = 'https://mock-endpoint.com/graphql'; }); it('返回正确的页面数据当请求成功时', async () => { // 模拟 fetch 成功响应 (fetch as jest.Mock).mockResolvedValue({ ok: true, json: jest.fn().mockResolvedValue(mockSuccessResponse) }); const result = await getData(mockTargetUrl); // 验证 fetch 调用参数是否正确 expect(fetch).toHaveBeenCalledWith(process.env.ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: GET_DATA, variables: { targetUrl: mockTargetUrl } }) }); expect(result).toEqual(mockSuccessResponse.data.page); }); it('抛出错误当响应状态异常时', async () => { (fetch as jest.Mock).mockResolvedValue({ ok: false }); await expect(getData(mockTargetUrl)).rejects.toThrow('Unable to parse json response'); }); it('抛出错误当 GraphQL 返回错误信息时', async () => { (fetch as jest.Mock).mockResolvedValue({ ok: true, json: jest.fn().mockResolvedValue({ errors: ['请求失败'] }) }); await expect(getData(mockTargetUrl)).rejects.toThrow('Something went wrong'); }); });
3. getRedirectUrl 函数的单元测试
通过 jest.mock 完全隔离 getData 依赖,只测试重定向逻辑:
import getRedirectUrl from './getRedirectUrl'; import getData from './getData'; // mock getData 模块 jest.mock('./getData'); describe('getRedirectUrl', () => { const mockValidFooUrl = '/foo/path/6486867645846'; const mockNonFooUrl = '/bar/path'; const mockMyPageData = { content: { type: 'MyPage', id: 'e523f6f6-6e73-4bb0-8737-95b63e1f9e6c' } }; const mockOtherPageData = { content: { type: 'OtherPage', id: '123' } }; beforeEach(() => { jest.clearAllMocks(); }); it('生成正确的重定向URL当路径包含/foo/且内容为MyPage时', async () => { (getData as jest.Mock).mockResolvedValue(mockMyPageData); const result = await getRedirectUrl(mockValidFooUrl); expect(getData).toHaveBeenCalledWith(mockValidFooUrl); expect(result).toBe('/my-page/?id=e523f6f6-6e73-4bb0-8737-95b63e1f9e6c'); }); it('返回null当路径包含/foo/但内容不是MyPage时', async () => { (getData as jest.Mock).mockResolvedValue(mockOtherPageData); const result = await getRedirectUrl(mockValidFooUrl); expect(result).toBeNull(); }); it('返回null当路径不包含/foo/时', async () => { const result = await getRedirectUrl(mockNonFooUrl); expect(getData).not.toHaveBeenCalled(); expect(result).toBeNull(); }); });
核心注意事项
- 单元测试要隔离依赖:测试
getRedirectUrl时不要触发实际网络请求,通过 mock 切断依赖链。 - 异步测试必须用
async/await:确保 Jest 等待异步操作完成,避免断言提前执行。 - 清理 mock 状态:每次测试前用
jest.clearAllMocks()清除调用记录,防止测试间互相干扰。
内容的提问来源于stack exchange,提问作者meez
相关产品推荐
相关产品推荐

