使用 Jest 模拟模块无法正常工作

Mocking a module with Jest not working properly

我正在尝试模拟一个模块,这里是来自 aws-sdk 的 S3。

我唯一能让它工作的方法是这样的:

jest.mock('aws-sdk', () => {
  return {
    S3: () => ({
      putObject: jest.fn()
    })
  };
});

问题是我无法访问 S3 或 putObject 作为模拟变量,我可以检查是否出于测试目的调用它。

所以我想做这样的事情:

const putObject = jest.fn();

jest.mock('aws-sdk', () => {
  return { S3: () => ({ putObject }) };
});

我总是为其他模块这样做并且它工作得很好,它甚至适用于 lambda,但不适用于这种情况。

对我来说,这两个代码看起来完全一样,所以我真的不明白这是怎么回事,为什么它的工作方式不完全一样。

当我在要测试的代码中使用控制台记录 s3 时,我得到:

{ putObject: undefined }

此外,我使用 TypeScript,所以我不能只在测试文件中导入模块,如果它不是模拟变量,我就不能模拟返回值。

感谢您的帮助!

编辑:

这是问题的可重现的最小示例:

uploadFile.ts

import AWS from 'aws-sdk';

const s3 = new AWS.S3();

export const uploadFile = async (key, body) => {
  try {
    await s3
      .putObject({
        Bucket: 'myBucket',
        Key: key,
        Body: body,
      })
      .promise();
  } catch (error) {
    console.log(error);
  }
};

uploadFile.test.ts

import { uploadFile } from './uploadFile';

const mockPutObject = jest.fn(() => ({ promise: jest.fn() }));

jest.mock('aws-sdk', () => {
  return { S3: () => ({ putObject: mockPutObject }) };
});

describe('Test uploadFile', () => {
  it('should call putObject', () => {
    uploadFile('key', { test: 'test' });
    expect(mockPutObject).toHaveBeenCalled();
  });
});

我是这样实现的:

import * as AWS from 'aws-sdk';
import { uploadFile } from '.';

const mockPutObject = jest.fn().mockImplementation((data) => {
    return {
        promise: () => jest.fn()
    }
});

jest.mock('aws-sdk', () => {
    return {
        S3: function () {
            return {
                putObject: (data: any) => mockPutObject(data)
            }
        }
    };
});

describe('Test uploadFile', () => {
    it('should call putObject', () => {
        uploadFile('key', { test: 'test' });
        expect(mockPutObject).toHaveBeenCalledTimes(1);
        expect(mockPutObject).toHaveBeenCalledWith({
            Bucket: 'myBucket',
            Key: 'key',
            Body: {
                test: 'test'
            },
        });
    });
});

有一些您可能没有的挑战:

  • AWS.S3 是一个具有构造函数的 class,但我找不到有效的 tsconfig 或 babel 配置。因此我将一些 () => {...} 替换为 function () { ... }
  • 开玩笑提升属性的方式使 return mockPutObject 变得棘手,因为它在构建模拟时尚未初始化
  • mock 需要 return promise() mock 以及与您的 index.ts
  • 一起工作