使用Jest模拟S3Client实例的send方法时遭遇“send属性不存在”错误,寻求解决方案
使用Jest模拟S3Client实例的send方法时遭遇“send属性不存在”错误,寻求解决方案
我完全理解你现在遇到的困扰,咱们一步步拆解问题,找到解决办法:
首先分析错误原因
你尝试用jest.spyOn(S3Client, "send")来模拟失败场景,但这里犯了一个关键错误:send是S3Client实例的方法,不是S3Client构造函数的静态方法,所以直接spyOn构造函数本身,自然会提示“send属性不存在”。
另外还要提一句,你当前代码里的files.forEach(async (file) => {...})存在异步逻辑的问题:forEach不会等待内部的异步操作完成,而且try/catch也捕获不到forEach里的Promise错误,就算S3的send方法reject了,你的catch块也不会触发,这会导致测试用例里的502状态永远不会返回,这个问题也得一起修复。
解决方案一:提前定义可修改的send模拟函数
我们可以把S3Client实例的send方法单独抽成一个可复用的mock函数,这样在测试用例里就能轻松修改它的实现:
// 先定义一个可控制的send方法模拟 const mockSend = jest.fn(() => "send"); jest.mock("@aws-sdk/client-s3", () => ({ PutObjectCommand: jest.fn(), S3Client: jest.fn(() => ({ send: mockSend, // 把模拟的send方法绑定到实例上 })), })); describe("upload product", () => { it("should send status of 502 on failing on saving img", async () => { // 先把send方法改成reject的状态,模拟上传失败 mockSend.mockRejectedValueOnce("not saved"); await uploadProductHandler(mockReq, mockRes); // 你的断言逻辑 expect(generateRandom).toHaveBeenCalledTimes(3); expect(generateRandom).toHaveBeenLastCalledWith(32); expect(PutObjectCommand).toHaveBeenCalledTimes(2); // 别忘了断言返回的502状态 expect(mockRes.sendStatus).toHaveBeenCalledWith(502); }); });
解决方案二:在mock内部暴露send模拟函数
如果不想提前定义全局的mockSend,也可以在mock模块里把send方法暴露出来,供测试用例调用:
jest.mock("@aws-sdk/client-s3", () => { const mockSend = jest.fn(() => "send"); return { PutObjectCommand: jest.fn(), S3Client: jest.fn(() => ({ send: mockSend, })), // 把模拟函数暴露给测试用例 __mockSend: mockSend, }; }); // 导入暴露的模拟函数 const { __mockSend } = require("@aws-sdk/client-s3"); describe("upload product", () => { it("should send status of 502 on failing on saving img", async () => { __mockSend.mockRejectedValueOnce("not saved"); await uploadProductHandler(mockReq, mockRes); // 你的断言逻辑... expect(mockRes.sendStatus).toHaveBeenCalledWith(502); }); });
必须修复的异步逻辑问题
刚才提到的forEach异步问题,你需要把代码改成用Promise.all来等待所有上传操作完成,这样try/catch才能正确捕获错误:
try { // 把forEach改成map+Promise.all,确保所有异步上传都完成后再继续 await Promise.all(files.map(async (file) => { const imageName = generateRandom(32); images.push(imageName); const params = { Bucket: bucketName, Key: imageName, Body: file.buffer, ContentType: file.mimetype, }; // 这里你拼写错了command,也要修正 const command = new PutObjectCommand(params); await s3.send(command); })); } catch { return res.sendStatus(502); }
备注:内容来源于stack exchange,提问作者Dhuruv Bansal
相关产品推荐
相关产品推荐

