我正在为一段代码编写单元测试,该代码可以从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接口是一个简单的包装器)。
答案 0 :(得分:0)
我自己找到了解决方案:手动模拟。
在要模拟的文件旁边创建一个__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
。现在,笑话将加载模拟的util,而不是真实的util。
主要缺点:我无法检查调用各种方法的频率,但是对于我的具体情况,这不是问题。 (因为我也在模拟对数据库的访问,因此我将数据推送到该数据库,但我仍然可以将该模拟传递给jest.fn()
。)