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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 22:07:50