Doubts in s3 client test

39 views Asked by At

I have the following code and unit tests. Its a simple lambda handler function to download a object from S3 bucket. I am unable to successfully write unit tests for it. Need help in debugging.

Handler code

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);
    }
}

Unit test

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();
    });
}

The test is failing with this error.

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();

When I use the debugger locally, I see its hitting the send but the test is not able to identify. What am I missing??

1

There are 1 answers

0
Shigerello On

I debugged the test code, and noticed that GetObjectCommand and S3Client are not mocked. To mock modules, it seems jest.mock() has to be called at the root of the test module.

https://github.com/kentcdodds/how-jest-mocking-works

So it turns out that if you make a call to jest.mock in the root of your module, that mocking will take place before any require statements are resolved/run.

To pass the test, I made adjustments shown below (look for 4 exclamation marks).

Handler code

const s3Client = new S3Client();  // !!!! Added this line
const s3Data = await s3Client.send(getObjectCommand);

Unit test

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

// !!!!
// Moved module mocking to the root of the test module
// !!!!
const mockS3Client = {
    send: jest.fn().mockResolvedValue({
        /* Mocked response */
    }),
};
jest.mock("@aws-sdk/client-s3", () => ({
    S3Client: jest.fn(() => mockS3Client),
    GetObjectCommand: jest.fn(),
}));

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", () => {
    beforeEach(() => {
        jest.clearAllMocks();
    });

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