Node.js 14环境下使用AWS SDK v3调用transformToWebStream报错及单元测试问题
Node.js 14环境下使用AWS SDK v3调用transformToWebStream报错及单元测试问题
我完全懂你现在的困扰——在Node.js 14环境下用AWS SDK v3做S3文件下载的单元测试时,调用transformToWebStream()触发了版本兼容报错,提示Readable.toWeb() is not supported。这是因为transformToWebStream()底层依赖Node.js 17+才引入的stream.Readable.toWeb()方法,而Node.js 14原生并没有这个API,直接用原生Readable包装的SDK Stream自然会报错。
下面给你两个实用的解决办法,还有一个测试里的小坑要注意:
解决方案一:直接Mock符合要求的SDK Stream对象
最直接的方式是不用原生Node Stream,而是自己创建一个Mock对象,直接实现transformToWebStream()方法,完全绕开版本兼容问题。修改你的测试代码如下:
it('should be able to downloadFromS3()', async () => { // 创建一个Mock的SDK Stream,直接实现transformToWebStream方法 const mockSdkStream = { transformToWebStream: () => { // 返回一个模拟的Web ReadableStream,对应你要测试的内容 return new ReadableStream({ start(controller) { // 把测试用的字符串转成Uint8Array,模拟文件内容 controller.enqueue(new TextEncoder().encode('hello world')); controller.close(); } }); } }; const mockS3Client = mockClient(S3Client); // 只需要Mock GetObjectCommand,你的生产代码里没调用HeadObject mockS3Client.on(GetObjectCommand).resolves({ Body: mockSdkStream }); const result = await downloadFromS3({ name: 'name', s3Key: 's3Key', } as Document); // 只断言GetObjectCommand被调用即可 expect(mockS3Client).toHaveReceivedCommand(GetObjectCommand); });
解决方案二:给Node.js 14添加toWeb方法的Polyfill
如果希望保留原生Stream的使用方式,可以给Node.js 14的Readable原型添加toWeb()的polyfill,让SDK的transformToWebStream()能正常调用。在测试文件顶部添加这段代码:
import { Readable } from 'stream'; // 给Node.js 14的Readable添加toWeb方法的polyfill if (!Readable.prototype.toWeb) { Readable.prototype.toWeb = function() { return new ReadableStream({ async start(controller) { try { // 遍历Node流的内容,转发到Web流 for await (const chunk of this) { controller.enqueue(chunk); } controller.close(); } catch (err) { controller.error(err); } } }); }; }
之后你原来的测试代码就可以正常运行了,sdkStreamMixin包装的原生Readable现在能调用toWeb()方法,不会再报错。
额外注意:移除不必要的HeadObjectCommand断言
看你的测试代码里Mock了HeadObjectCommand还加了断言,但你的生产代码downloadFromS3()里并没有调用这个命令,这会导致测试失败哦。记得把这两行删掉:
mockS3Client.on(HeadObjectCommand).resolves({ /* 模拟响应 */ }); expect(mockS3Client).toHaveReceivedCommand(HeadObjectCommand);
备注:内容来源于stack exchange,提问作者LP13




