You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 16:18:03