node.js - 开玩笑地模拟复杂依赖项(Papa.parse 的 S3 访问)
问题描述
我正在为一段代码编写单元测试,该代码从 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 接口是一个简单的包装器。)
解决方案
我自己找到了解决方案:手动模拟。
__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。
主要缺点:我无法检查调用各种方法的频率,但对于我的具体情况,这不是问题。(因为我也在模拟对数据库的访问,我将这些数据推送到,我仍然可以通过那个模拟 a jest.fn()
)。
推荐阅读
- c# - 尝试使用 React.js 和 Redux 将 Azure B2c 身份验证添加到 Asp.net 核心
- git - Git - 切换到其他分支会显示上一个分支的修改
- csv - 由于 Null 数据,没有仪表板报告
- mysql - 编写一个查询,根据监视器的数量(monitorID)为每个用户(userID)创建新列
- networking - 在接收端选择哪种数据结构进行 ip 数据报重组?
- asp.net-mvc - RedirectToAction 正在返回 HTML 内容
- sql - SQL (bigquery) - SUM 在时间间隔内以开始/停止 DATETIME 继续值
- asp.net-core - EF Core AutoMapper:未应用包含中的过滤器嵌套集合
- templates - C ++提取元组元组中每个元组的第k个元素
- php - xampp phpmyadmin 中缺少 mysqli 扩展