【问题标题】:Mocking complex dependency (S3 access for Papa.parse) in jest开玩笑地模拟复杂依赖项(Papa.parse 的 S3 访问)
【发布时间】:2020-10-22 17:20:50
【问题描述】:

我正在为一段代码编写单元测试,该代码从 AWS S3 存储桶加载文件并对其进行处理。处理由 Papa.parse 通过 createReadStream 完成。我担心我可能不得不模拟 S3 文件和 Papa.parse 之间的交互。

我的代码:

const { reader, s3 } = require('../util');

file = await reader(
  SOURCE_BUCKET_NAME,
  SOURCE_BUCKET_PREFIX,
  FILE_PREFIX,
  FILE_SUFFIX,
);

const s3file = s3.getObject({ Bucket: SOURCE_BUCKET_NAME, Key: file.Key });

return new Promise((resolve, reject) => {
  Papa.parse(s3file.createReadStream().pipe(zlib.createGunzip()), {
    encoding: 'utf8',
    header: true,
    step: (line) => {
      const d = line.data[0];
      // handling irrelevant to the mock issue
    },
    complete: async () => {
      // more handling
    },
  });
});

reader() 是一个实用函数,它包装了一些检查和 s3 请求并返回我们要加载的文件。 s3 是由导入的实用程序实例化的实际 AWS s3 对象。

在我的测试中,我根本不想使用真正的 s3,所以我想模拟 reader() 函数和 s3 对象,其中我只调用 s3.getObject。

所以我就是这样嘲讽的:

const util = require('../util');

describe('blah', () => {
  beforeEach(() => {
    jest.mock('../util', () => jest.fn());

    const mockReadStream = jest.fn().mockImplementation(() => {
      const readable = new Readable();
      readable.push('fieldA, fieldB\n');
      readable.push('value A1, value B1\n');
      readable.push('value A2, value B2\n');
      readable.push(null);
      return readable;
    });

    s3GetObject = jest.fn(() => ({
      createReadStream: fn(() => ({
        pipe: mockReadStream,
      })),
    }));
    util.reader = jest.fn((bucketName, bucketPrefix, filePrefix, fileSuffix) => ({
      Key: `/${filePrefix}__20201021.${fileSuffix}`,
    }));
    util.s3 = jest.fn(() => ({
      getObject: s3GetObject,
    }));
  });
});

据我在网上找到的,这应该有效,但它没有。单元代码从真正的 S3 存储桶加载实际文件,而不是我的模拟。

问题是,我正在使用相同的模拟方式(const {x} = require(y) 和测试中的y.x = jest.fn(),它工作正常。虽然我也在某个地方使用它,如果我模拟它就不起作用一个导入,但是如果我模拟第一个导入所依赖的辅助导入,它确实有效。我不知道为什么,但我的解决方法有效,所以我不担心。但这一次它根本不起作用,并且真的不想二次依赖,因为那样我就不得不模拟整个 S3 接口。(我在这里导入的 S3 接口是一个简单的包装器。)

【问题讨论】:

  • 首先,你的被测代码不完整。
  • 在这里发布整个文件并不是很有用。这是显示它如何被调用的相关位。我想我可以扩展一点,因为我试图模拟的同一个导入中还有更多功能,但它们更简单。如果我能解决这个问题,其他的就不是问题了。

标签: node.js amazon-web-services unit-testing amazon-s3 jestjs


【解决方案1】:

我自己找到了解决方案:手动模拟。

在我要模拟的文件旁边创建一个__mocks__ 文件夹,在其中放入一个同名文件,以及这些内容:

const { Readable } = require('stream');

const mockReadStream = jest.fn().mockImplementation(() => {
  const readable = new Readable();
  readable.push('fieldA, fieldB\n');
  readable.push('value A1, value B1\n');
  readable.push('value A2, value B2\n');
  readable.push(null);
  return readable;
});

const s3GetObject = () => ({
  createReadStream: () => ({
    pipe: mockReadStream,
  }),
});

const s3 = {
  getObject: s3GetObject,
};

const reader = async (bucketName, dirPrefix = '/', filePrefix, fileSuffix) => ({
  Key: `/${filePrefix}__20201021_2020102112345.${fileSuffix}`,
});

module.exports = {
  reader,
  s3,
};

然后在单元测试文件中,开始:

jest.mock('../../datamigrations/util');

删除所有其他模拟代码和原始require。现在 jest 将加载模拟的 util 而不是真正的 util。

主要缺点:我无法检查调用各种方法的频率,但对于我的具体情况,这不是问题。 (因为我也在模拟对数据库的访问,我将这些数据推送到,我仍然可以将那个模拟传递给jest.fn())。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-22
    • 2017-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 2020-03-05
    • 1970-01-01
    相关资源
    最近更新 更多