Jest运行模拟AWS S3文件读取测试超时,如何正确mock createReadStream?
问题根因
你当前对S3读取流的Mock仅定义了空的on函数,没有主动触发data、end事件,导致getBufferFromS3内的回调永远不会执行,包裹的Promise长期处于pending状态,最终触发超时。
解决方案
提供两种可选方案,你可以根据场景选择:
方案1:修复S3流的Mock逻辑
不需要修改业务代码,直接调整测试里的S3 Mock配置,让流主动触发对应事件:
// 模拟真实的xlsx文件buffer const mockXlsxBuffer = Buffer.from('fake xlsx content'); const mockgetObject = { createReadStream: jest.fn(() => { return { on: jest.fn((eventName, callback) => { // 触发data事件返回模拟内容 if (eventName === 'data') { callback(mockXlsxBuffer); } // 触发end事件标记读取完成 if (eventName === 'end') { callback(); } // 如需测试错误场景,可加eventName === 'error'分支传入错误对象 return this; }) } }) }
方案2:直接Mock getBufferFromS3Promise(更简洁)
如果允许小幅度调整业务代码,把内部的getBufferFromS3Promise抽成类的公有方法,就可以直接Mock跳过流处理逻辑,减少Mock复杂度:
第一步:调整业务代码handler.ts
将createWorkbookFromS3内部的getBufferFromS3Promise抽为Service类的公有方法:
class Service { client: any; workbook: any = ""; constructor(client) { this.workbook = ""; this.client = client; } // 抽出来的可Mock方法 async getBufferFromS3Promise(fileLocation: string) { const params = { Bucket: "bucket", Key: fileLocation, }; function getBufferFromS3(callback) { const buffers = []; const s3 = new AWS.S3(); const stream = s3.getObject(params).createReadStream(); stream.on("data", (data) => buffers.push(data)); stream.on("end", () => callback(null, Buffer.concat(buffers))); stream.on("error", (error) => callback(error)); } return new Promise((resolve, reject) => { getBufferFromS3((error, s3buffer) => { if (error) return reject(error); return resolve(s3buffer); }); }); } async createWorkbookFromS3(filePath: string, id: string) { const fileLocation = id + filePath; try { const buffer = await this.getBufferFromS3Promise(fileLocation); this.workbook = XLSX.read(buffer); } catch (e) { this.client.release() console.error(e); throw new Error("Unable to read the xlsx from S3"); } } }
第二步:测试代码直接Mock该方法
it('should execute decoder', async () => { const id = 'id'; const filePath = 'filePath' const service = new decoder.Service(client); // 直接Mock返回模拟buffer,不需要处理S3流逻辑 jest.spyOn(service, 'getBufferFromS3Promise').mockResolvedValue(Buffer.from('fake xlsx content')); await service.createWorkbookFromS3(filePath, id) // 剩余测试逻辑 })
其他需要修复的测试问题
就算流的问题修复,你现有测试代码还有以下问题会导致执行失败:
- 测试传入的event路径是
/decoder/draft,但业务代码里匹配的路径是/vsiDecoder/draft,路径不匹配会直接返回400 - 业务代码里
dbCredentials是硬编码的普通对象,没有SecretString属性,调用JSON.parse(dbCredentials['SecretString'])会报错,需要调整业务代码的dbCredentials定义,或者Mock对应的取值逻辑 - 调用
createWorkbookFromS3时参数传反了,方法定义第一个参数是filePath,第二个是id,你当前传的是(id, filePath) - 业务返回的是原生对象,你测试里期望的是JSON字符串,不需要序列化,直接对比对象即可
内容的提问来源于stack exchange,提问作者wezrine
相关产品推荐
相关产品推荐

