我有一个利用 Amplify 在 AWS 中运行的 Typescript/React 应用程序。 我的应用程序利用 Amplify API (GraphQL) 和 Amplify Storage 来与 Amazon 服务配合使用。
在 Amplify V5 中,我能够为相关 API 调用设置 Jest 函数,这样我就可以安全地运行单元测试。
setupTests.ts
import {API, Storage} from "aws-amplify";
/** Establish API mocking before all tests. */
beforeAll(() => {
URL.revokeObjectURL = jest.fn();
//Window.prototype.scroll = 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 从 V5 升级到 V6。 API 发生了变化。 而不是
import {API, Storage} from "aws-amplify";
然后在这些对象上运行函数,函数将直接导入。
import {generateClient} from 'aws-amplify/api'
import {getUrl, downloadData, copy, remove, uploadData} from 'aws-amplify/storage';
我尝试了一些不同的方法来模拟 setupTests.ts 中的generateClient,但没有任何效果。
jest.mock('aws-amplify');
jest.mock('aws-amplify/api');
jest.mock('aws-amplify/storage');
jest.mock('aws-amplify/api',
() => { return { generateClient: () => { graphql: jest.fn() } };
});
jest.mock('generateClient', () => { graphql: jest.fn(); });
jest.mock('aws-amplify/api', () => {
return { generateClient: () => { graphql: jest.fn() } };
});
import * as API from 'aws-amplify/api';
beforeAll(() => {
API.generateClient = jest.fn();
const generateClient = jest.mocked(API).generateClient;
const generateClient = API.generateClient;
});
jest.mocked(generateClient).mockImplementation(() => ({graphql: jest.fn()}));
jest.mock('aws-amplify/auth');
jest.mock('getUrl', () => jest.fn());
jest.mock('downloadData', () => jest.fn());
jest.mock('uploadData', () => jest.fn());
jest.mock('copy', () => jest.fn());
jest.mock('remove', () => jest.fn());
// @ts-ignore
generateClient.mockImplementation(() => ({graphql: jest.fn()}));
// @ts-ignore
getUrl.mockImplementation(() => {});
// @ts-ignore
downloadData.mockImplementation(() => {});
// @ts-ignore
uploadData.mockImplementation(() => {});
// @ts-ignore
copy.mockImplementation(() => {});
// @ts-ignore
remove.mockImplementation(() => {});
测试时
expect(client.graphql).not.toHaveBeenCalled();
其中 client = generateClient();
和稍后在相同的 beforeAll
方法中或单独的测试文件中。我不断收到错误,指出 client.graphQL
是实际函数而不是模拟函数,在 *.mockImplementation()
示例中,错误是关于该函数不可用,因为代码尚未被模拟。
TL/DR: 在 Amplify V6 中,使用直接方法和生成器导出,如何将测试设置为仅使用 Jest Mock 函数来代替生产代码中的实际调用以进行单元测试?
更新:
如果我放
jest.mock('aws-amplify/api', () => ({
generateClient: jest.fn(() => { graphql: jest.fn() }),
}));
//@ts-ignore
when(generateClient).calledWith().mockReturnValue({ graphql: jest.fn() });
在我的测试课中
describe(
之前,它成功地嘲笑了generateClient()
和client.graphql(
。beforeAll(
。
上面的代码在
beforeAll(
中不起作用,所以我如何在一个地方安全地存储和设置模拟代码,这样我就不会复制一堆代码来到处设置模拟?
更新2 根据请求,最少的测试代码:
import {generateClient} from "aws-amplify/api";
jest.mock('aws-amplify/api');
const client = generateClient();
describe('App', () => {
test('mocks Amplify correctly', () =>
{
console.log(`generateClient: ${generateClient}`);
expect(jest.isMockFunction(generateClient)).toBeTruthy();
expect(jest.isMockFunction(client)).toBeFalsy();
console.log(`client: ${client}`);
expect(client).toHaveProperty('graphql');
expect(jest.isMockFunction(client.graphql)).toBeTruthy()
expect(client.graphql).not.toHaveBeenCalled();
});
});
这就是我一直用来验证我是否正确构建了预期的 Mock 对象的方法。
我有一些辅助文件和其他测试,它们使用
when()
告诉模拟对象返回预设的测试数据。
像这样:
test('Documents still display when getDocuments returns an error.',
async () =>
{
//setup mocking, with forced error
when(client.graphql)
.calledWith(expect.objectContaining({query: queries.listDocumentDetails} ))
.mockRejectedValue(errorDocList);
const { store } = renderPage(DASHBOARD_PATH, <Dashboard />, state);
//check page renders
expect(screen.getByText(RecentDocumentsTitle)).toBeInTheDocument();
//check for error message
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();
};
我找到了我的问题。
解决方案是手动模拟。 文档说在模拟模块中的代码时,在
__mocks__
旁边创建一个 node_modules
文件夹。这对我的项目不起作用。解决方案是将 __mocks__
文件夹放在我的项目的 src
目录的根目录下。