Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I mock AWS S3 GetObjectCommand with jest using the v3 sdk?

Testing an s3 upload? The method to test is

export class ProcessData {
  constructor() {}

  async process(): Promise<void> {
     const data = await s3Client.send(new GetObjectCommand(bucket));
     await parseCsvData(data.Body)
}

This is my attempt at the test case.

import {S3Client} from '@aws-sdk/client-s3';
jest.mock("aws-sdk/clients/s3", () => {
  return {
    S3Client: jest.fn(() => {
        send: jest.fn().mockImplementation(() => {
            data:  Buffer.from(require("fs").readFileSync(path.resolve(__dirname, "test.csv")));
        })
    })
  }
});

describe("@aws-sdk/client-s3 mock", () => {
  test('CSV Happy path', async () => {
    const processData = new ProcessData()
    await processData.process()
  }
}

The process gets to the parse method and throws an error "The bucket you are attempting to access must be addressed using the specific endpoint"

like image 957
Interlated Avatar asked Sep 12 '25 16:09

Interlated


2 Answers

For anyone who wants to mock the client directly, you can use the library aws-sdk-client-mock which is recommended by the AWS SDK team.

Here is an introductory tutorial

The initial steps:

import fs from 'fs';

import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { mockClient } from 'aws-sdk-client-mock';

const mockS3Client = mockClient(S3Client);

And then you can mock it this way

mockS3Client.on(GetObjectCommand).resolves({
  Body: fs.createReadStream('path/to/some/file.csv'),
});

You can also spy on the client

const s3GetObjectStub = mockS3Client.commandCalls(GetObjectCommand)

// s3GetObjectStub[0] here refers to the first call of GetObjectCommand
expect(s3GetObjectStub[0].args[0].input).toEqual({
  Bucket: 'foo', 
  Key: 'path/to/file.csv'
});

Update May 6th, 2023

Since version 3.188.0 (Oct 22), the S3 client supports util functions to consume and parse the response streams.

So now, to mock the response, we need to wrap it with the util function sdkStreamMixin() from @aws-sdk/util-stream-node

Update Jan 28th, 2024

As mentioned by @Matthew Woo, the @aws-sdk/util-stream-node package is deprecated and we should use the sdkStreamMixin() function from @smithy/util-stream instead

import fs from 'fs';

import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
import {sdkStreamMixin} from '@smithy/util-stream';
import { mockClient } from 'aws-sdk-client-mock';

const mockS3Client = mockClient(S3Client);
const stream = sdkStreamMixin(fs.createReadStream('path/to/some/file.csv'))

mockS3Client.on(GetObjectCommand).resolves({
  Body: stream ,
});

Update 18 Apr 2024

The current docs for aws-sdk-client-mock for this are a little more complex:

import {GetObjectCommand, S3Client} from '@aws-sdk/client-s3';
import {sdkStreamMixin} from '@smithy/util-stream';
import {mockClient} from 'aws-sdk-client-mock';
import {Readable} from 'stream';
import {createReadStream} from 'fs';

const s3Mock = mockClient(S3Client);

it('mocks get object', async () => {
    // create Stream from string
    const stream = new Readable();
    stream.push('hello world');
    stream.push(null); // end of stream

    // alternatively: create Stream from file
    // const stream = createReadStream('./test/data.txt');

    // wrap the Stream with SDK mixin
    const sdkStream = sdkStreamMixin(stream);

    s3Mock.on(GetObjectCommand).resolves({Body: sdkStream});

    const s3 = new S3Client({});

    const getObjectResult = await s3.send(new GetObjectCommand({Bucket: '', Key: ''}));

    const str = await getObjectResult.Body?.transformToString();

    expect(str).toBe('hello world');
});
like image 148
akmalzamri Avatar answered Sep 15 '25 07:09

akmalzamri


Applying the example worked:

https://github.com/awsdocs/aws-doc-sdk-examples/blob/master/javascriptv3/example_code/s3/tests/s3_putbucketpolicy.test.js

import {s3Client} from '../clients/s3Client';
jest.mock("../clients/s3Client.ts");

describe("@aws-sdk/client-s3 mock", () => {
  test('CSV Happy path', async () => {
    // @ts-ignore
    s3Client.send.mockResolvedValue({
      Body: Buffer.from(require("fs").readFileSync(path.resolve(__dirname, "nrl-small2.csv")))
    })
 })
like image 37
Interlated Avatar answered Sep 15 '25 06:09

Interlated