【问题标题】:Mocking a module with Jest not working properly用 Jest 模拟模块无法正常工作
【发布时间】:2021-11-06 00:14:10
【问题描述】:

我正在尝试模拟一个模块,这里是来自 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,所以我不能只导入测试文件中的模块,如果它不是模拟变量,我就不能模拟返回值。

感谢您的帮助!

编辑:

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

上传文件.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);
  }
};

上传文件.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();
  });
});

【问题讨论】:

  • 您可以尝试将putObject 重命名为mockPutObject。开玩笑有一种机制可以保护未初始化的变量,您需要为所有变量添加前缀以在模拟模块中使用。
  • 显示代码,创建一个最小的、可重现的示例
  • @Sirode 我试过了,我得到了通常的“s3.putObject 不是函数”
  • @slideshowp2 我刚刚添加了示例,抱歉耽搁了
  • 在模拟模块之前需要添加import AWS from 'aws-sdk';

标签: node.js typescript unit-testing jestjs aws-sdk


【解决方案1】:

我能够让它这样工作:

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 是一个具有构造函数的类,我找不到有效的 tsconfig 或 babel 配置。因此我将一些() => {...} 替换为function () { ... }
  • 开玩笑提升属性的方式使得返回 mockPutObject 变得很棘手,因为它在构建模拟时尚未初始化
  • 模拟需要返回 promise() 模拟以及与您的 index.ts 一起使用

【讨论】:

  • 谢谢!它可以工作,但是您将如何检查参数 putObject 已被调用?因为我尝试了 expect(mockPutObject).toHaveBeenCalledWith(...) 但它说它是用 0 个参数调用的。
  • 我已经编辑过了。我希望这将帮助您进入正确的方向。我建议您阅读一些包含更多示例的材料,以便您可以将它们调整到您的测试用例中。
猜你喜欢
  • 2021-06-29
  • 1970-01-01
  • 2021-08-02
  • 1970-01-01
  • 1970-01-01
  • 2013-02-18
  • 2020-08-14
  • 2015-09-27
  • 1970-01-01
相关资源
最近更新 更多