How to mock AWS SDK with Jest
Rita {FlyNerd} Lyczywek

Rita {FlyNerd} Lyczywek @ritaly

About: Backend is my comfort zone 💜

Location:
Poland
Joined:
Apr 13, 2019

How to mock AWS SDK with Jest

Publish Date: Jun 24 '22
7 3

From me to future me - how to write unit test for AWS SDK S3 presigned url with Jest

Dear Journal 📖

From me to future me: how to write unit test for AWS SDK S3 by example (presigned url with Jest).

I hate mocks.
That's why I always forgot how to use them when I need them.

Oh Rita, have you tried to spyOn AWS SDK service again? 🤦‍♀️

🛑 STOP

You need to either:

  • mock element on the object (when you have an object)
  • mock whole file (@aws-sdk/s3-request-presigner). I do hate that, but I guess you don't have that much choice, unless you want to wrap it in something else 🤷‍♂️

This is one of the reasons OOP and dependency injection (even manual one) is better (personal preference). I can define unit, its dependencies and then in tests I simply deliver something that fulfils the contract. No need to overwrite objects, files, etc. 🚫 No mocks.

But sometimes there's no other option.

Let's say I have a javascript or typescript file.

// handler.ts

import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { GetObjectCommand } from '@aws-sdk/client-s3';

export const handler: Handler = async () => {
  /* do stuff */
  const command = new GetObjectCommand(input);
  const url = await getSignedUrl(client, command, { expiresIn: 1200 });
  /* do other stuff */
}

Enter fullscreen mode Exit fullscreen mode

Then in spec file:

  • create an mock of whole the module
  • use empty mock function as you wish
// handler.spec.ts

jest.mock('@aws-sdk/s3-request-presigner');
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

jest.mock('@aws-sdk/client-s3');
import { GetObjectCommand } from '@aws-sdk/client-s3';

import { handler } from './handler';

test('when sth do sth', async () => {
  const expectedInput = { ... };
  await handler();
  expect(GetObjectCommand).toHaveBeenCalledWith(expectedInput);
  expect(getSignedUrl).toHaveBeenCalled();
});

Enter fullscreen mode Exit fullscreen mode

Honestly, it doesn't test this function. The fact that sth was called is not enough to confirm that expected behavior happened. It comes to the absurd: to get "real" behavior I need to mock the getSignerUrl() response.

So, I come to the point, where I write the mock, to test the function response, which is response from another mock, but technically is the nearest simulation of the function response and expected output.

// handler.spec.ts

jest.mock('@aws-sdk/s3-request-presigner');
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

jest.mock('@aws-sdk/client-s3');
import { GetObjectCommand } from '@aws-sdk/client-s3';

import { handler } from './handler';
describe('GetUrl', () => {
  const getSignedUrlMock: jest.Mock = getSignedUrl as any; // calm down TypeScript screaming about types

  test('when sth do sth', async () => {
    getSignedUrlMock.mockResolvedValue('example-url.com');
    const expectedInput = { ... };
    const response = await handler();

  expect(GetObjectCommand).toHaveBeenCalledWith(expectedInput);
    expect(response).toEqual({
      statusCode: 200,
      body: JSON.stringify({ url: 'example-url.com' }),
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

Hope, that next time you will thank yourself for this

Sincerely yours
Rita

Comments 3 total

  • danielransome
    danielransomeFeb 14, 2023

    Hi Rita, this was a real help, thanks! Mocking AWS has been quite a headache for me. I found aws-sdk-client-mock to be a really helpful lib but I don't think it works in this scenario and I've ended up mocking as per your example code.

  • mat
    matFeb 12, 2024

    Hey Rita, any idea why i see
    TypeError: getSignedUrlMock.mockResolvedValue is not a function
    ?
    the getSignedUrlMock itself is [AsyncFunction: getSignedUrl]

Add comment