如何在Amplify V6中用Jest全局模拟API与Storage做单元测试?
全局配置Amplify V6的Jest模拟(避免测试代码重复)
问题背景
我有一个基于TypeScript/React的AWS Amplify应用,使用Amplify API(GraphQL)和Amplify Storage对接AWS服务。在Amplify V5中,可在setupTests.ts里通过Jest模拟相关API调用以安全执行单元测试:
import {API, Storage} from "aws-amplify"; /** Establish API mocking before all tests. */ beforeAll(() => { URL.revokeObjectURL = jest.fn(); window.HTMLElement.prototype.scroll = jest.fn(); window.HTMLDivElement.prototype.scroll = jest.fn(); jest.mock('aws-amplify'); API.graphql = jest.fn(); Storage.get = jest.fn(); Storage.copy = jest.fn(); Storage.remove = jest.fn(); });
升级到Amplify V6后,API导入方式变更,需直接导入独立函数:
import {generateClient} from 'aws-amplify/api' import {getUrl, downloadData, copy, remove, uploadData} from 'aws-amplify/storage';
此前仅能在单个测试文件的describe前配置Mock,导致代码重复,希望在setupTests.ts中全局配置这些Jest模拟函数。
解决方案
1. 在setupTests.ts中配置全局Mock
直接在setupTests.ts中使用jest.mock全局模拟Amplify的api和storage模块,确保返回符合业务代码预期的结构:
import { jest } from '@jest/globals'; // 全局模拟Amplify GraphQL客户端生成函数 jest.mock('aws-amplify/api', () => ({ generateClient: jest.fn(() => ({ graphql: jest.fn() })) })); // 全局模拟Amplify Storage所有用到的函数 jest.mock('aws-amplify/storage', () => ({ getUrl: jest.fn(), downloadData: jest.fn(), copy: jest.fn(), remove: jest.fn(), uploadData: jest.fn() })); // 保留原有的全局Mock配置 beforeAll(() => { URL.revokeObjectURL = jest.fn(); window.HTMLElement.prototype.scroll = jest.fn(); window.HTMLDivElement.prototype.scroll = jest.fn(); });
2. 解决TypeScript类型报错(可选)
如果遇到类型不兼容的报错,可在项目根目录创建jest.d.ts文件,补充Mock的类型声明:
declare module 'aws-amplify/api' { export function generateClient(): { graphql: jest.Mock; }; } declare module 'aws-amplify/storage' { export const getUrl: jest.Mock; export const downloadData: jest.Mock; export const copy: jest.Mock; export const remove: jest.Mock; export const uploadData: jest.Mock; }
3. 测试文件中直接使用全局Mock
无需在每个测试文件重复编写jest.mock,直接导入并使用Mock函数即可,建议在beforeEach中重置Mock状态避免测试间污染:
import { generateClient } from "aws-amplify/api"; const client = generateClient(); describe('App', () => { beforeEach(() => { jest.clearAllMocks(); }); test('mocks Amplify correctly', () => { expect(jest.isMockFunction(generateClient)).toBeTruthy(); expect(client).toHaveProperty('graphql'); expect(jest.isMockFunction(client.graphql)).toBeTruthy(); expect(client.graphql).not.toHaveBeenCalled(); }); });
4. 业务测试中的Mock使用
在业务测试中直接对Mock函数设置返回值即可:
import { generateClient } from "aws-amplify/api"; import { queries } from './graphql/queries'; import { errorDocList } from './test-utils'; const client = generateClient(); test('Documents still display when getDocuments returns an error.', async () => { // 强制graphql返回错误 client.graphql.mockRejectedValueOnce(errorDocList); const { store } = renderPage(DASHBOARD_PATH, <Dashboard />, state); // 验证页面正常渲染 expect(screen.getByText(RecentDocumentsTitle)).toBeInTheDocument(); // 验证错误提示状态 const msg = `Failed to GET DocumentList: ${errorDocList.errors[0].message}`; const errorMsg = buildErrorAlert(msg); await waitFor(() => { expect(store.getState().alertMessage).toEqual(errorMsg); }); // 验证文档内容仍显示 const doc = errorDocList.data.listDocumentDetails.items[1]!; expect(screen.getByText(doc.eng_title)).toBeInTheDocument(); expect(screen.getByText(doc.bc_title)).toBeInTheDocument(); expect(screen.getByText(doc.ak_title)).toBeInTheDocument(); });
内容的提问来源于stack exchange,提问作者Raystorm
相关产品推荐
相关产品推荐

