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

Lambda S3对象下载函数单元测试失败,请求排查

S3下载Lambda函数Jest测试失败:mockS3Client.send未被调用

问题描述

我编写了一个从S3存储桶下载对象的Lambda处理函数,搭配Jest单元测试,但测试无法通过。报错显示mockS3Client.send方法未被调用,但本地调试时明明执行到了该方法,需要调试帮助。

处理函数代码

import type { GetObjectCommandInput } from "@aws-sdk/client-s3";
import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3";
import type { S3Event } from "aws-lambda";

async function handler(event: S3Event): Promise<void> {
    console.log("Received S3 event:", JSON.stringify(event));

    for (const record of event.Records) {
        const params: GetObjectCommandInput = {
            Bucket: record.s3.bucket.name,
            Key: record.s3.object.key,
        };

        const getObjectCommand = new GetObjectCommand(params);

        const s3Data = await s3Client.send(getObjectCommand);

        console.log(s3Data);
    }
}

单元测试代码

import { handler } from "~/resources/mons";
import { S3Event } from "aws-lambda";

const mockEvent: S3Event = {
    Records: [
        {
            eventVersion: "2.1",
            eventSource: "aws:s3",
            awsRegion: "us-east-1",
            eventTime: "2024-02-26T16:35:36.549Z",
            eventName: "ObjectCreated:Put",
            userIdentity: {
                principalId: "AWS:AROAW3MD7IRJM6QRM6RCR:abgaura-Isengard",
            },
            requestParameters: {
                sourceIPAddress: "72.21.198.71",
            },
            responseElements: {
                "x-amz-request-id": "0Q0FHDKGYS01M9G0",
                "x-amz-id-2":
                    "D6DaPnYNIw1F3shV+GZY4XdKhxt0hsWlrZf7JC1PIOm3IfrgQsG6Lekf4m1nqIM1RPiTDgUtM2iyzA2o2DwotZTtSYtZEt1H",
            },
            s3: {
                s3SchemaVersion: "1.0",
                configurationId: "NTYzOWU1NjQtMjY3Zi00OGNiLTk4ZWItNDljYjY1MjJjMTJl",
                bucket: {
                    name: "santosblueringwebapp-dev-471112565842-us-east-1",
                    ownerIdentity: {
                        principalId: "A3UY9USU3LZ6NZ",
                    },
                    arn: "arn:aws:s3:::santosblueringwebapp-dev-471112565842-us-east-1",
                },
                object: {
                    key: "dist/index.sece.html",
                    size: 1064,
                    eTag: "cf1a8087d548591d0328589e075a360d",
                    versionId: "8UCtubZ8etB_x6v1fdzJKLNffD8gsC0G",
                    sequencer: "0065DCBDD88107E020",
                },
            },
        },
    ],
};

describe("Handler", () => {
    const mockS3Client = {
        send: jest.fn().mockResolvedValue({
            /* Mocked response */
        }),
    };
    jest.mock("@aws-sdk/client-s3", () => ({
        S3Client: jest.fn(() => mockS3Client),
        GetObjectCommand: jest.fn(),
    }));

    beforeEach(() => {
        jest.clearAllMocks();
    });

    test("retrieves objects from S3 and transforms them to strings", async () => {
        await handler(mockEvent);
        expect(mockS3Client.send).toHaveBeenCalled();
    });
});

测试报错信息

expect(jest.fn()).toHaveBeenCalled()

    Expected number of calls: >= 1
    Received number of calls:    0

      60 |     test("retrieves objects from S3 and transforms them to strings", async () => {
      61 |         await handler(mockEvent);
    > 62 |         expect(mockS3Client.send).toHaveBeenCalled();

问题根源与修复方案

1. 处理函数未正确初始化S3Client实例

处理函数中直接使用了s3Client.send,但未声明或初始化该变量。本地调试时可能全局环境存在该实例,但测试环境中Jest模拟的S3Client未被正确赋值,导致调用的不是你定义的mock实例。

修复:在处理函数中显式创建S3Client实例:

// 显式创建S3Client实例
const s3Client = new S3Client({});

async function handler(event: S3Event): Promise<void> {
    // ... 原有代码逻辑
}

2. Jest模拟的作用域与时机问题

jest.mock会被提升到模块顶部执行,此时describe块内的mockS3Client还未定义,导致模拟的S3Client构造函数返回undefined,实际调用的不是你期望的mock实例。

修复方案A(推荐):在mock工厂函数内部定义mock实例

describe("Handler", () => {
    let mockSend: jest.Mock;

    // 在mock工厂内创建并暴露mock方法
    jest.mock("@aws-sdk/client-s3", () => {
        mockSend = jest.fn().mockResolvedValue({});
        return {
            S3Client: jest.fn(() => ({ send: mockSend })),
            GetObjectCommand: jest.fn(),
        };
    });

    beforeEach(() => {
        jest.clearAllMocks();
    });

    test("retrieves objects from S3 and transforms them to strings", async () => {
        await handler(mockEvent);
        // 验证mockSend被调用
        expect(mockSend).toHaveBeenCalled();
        // 可额外验证GetObjectCommand的调用参数
        expect(GetObjectCommand).toHaveBeenCalledWith(expect.objectContaining({
            Bucket: mockEvent.Records[0].s3.bucket.name,
            Key: mockEvent.Records[0].s3.object.key
        }));
    });
});

3. 补充验证逻辑

测试中可增加对GetObjectCommand的调用验证,确保参数传递正确,进一步定位问题。

内容的提问来源于stack exchange,提问作者Let us see.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 20:55:53