How to Mock AWS S3 in a Jest (Unit) Test

By Nikhil Vijayan

May 7th, 2021

image

I found it a bit hard to find out how to mock AWS S3 while using Jest. So, I thought I’d document it here.

Useful Stack Overflow answers:

How to mock S3 with jest? Jest Mock module per test

Here’s the code we will be testing

const AWS = require('aws-sdk')

// a function that returns a promise
function copyFileToS3() {
  
  // initialise AWS S3
  const s3 = new AWS.S3({
    accessKeyId: 'whatever',
    secretAccessKey: 'whatever',
    endpoint: 'http://localhost:4566', // localstack is running on port 4566. Imagine this was the real S3 endpoint
    s3ForcePathStyle: true,
  })
  
  const copyObjectParams = {
    Bucket: 'some-bucket',
    CopySource: 'some-bucket/some/path/myfile.json,
    Key: 'some-bucket/some/other/path/myfile.json',
  }
  
  return s3.copyObject(copyObjectParams).promise()
}

Here’s a unit test for the code above using Jest

We’ll be mocking S3, and asserting that S3’s copyObject function gets called with the right parameters.

Obviously, this is a simplistic function with hard-coded values, but the test is valid for a function that maybe does some transformation to the destination key, or does some validation on the CopySource, etc.

const s3 = require('./s3') // This is the file where we use aws-sdk's S3 CopyObject method

const mockS3Instance = {
  copyObject: jest.fn().mockReturnThis(),
  promise: jest.fn().mockReturnThis(),
  catch: jest.fn(),
}

jest.mock('aws-sdk', () => {
  return { S3: jest.fn(() => mockS3Instance) }
})

describe('S3', () => {
  it('calls aws-sdk copyObject method with correct parameters, async () => {
      await s3() // This is the function that uses the copyObject method
  
      expect(mockS3Instance.copyObject).toHaveBeenCalledWith({
        Bucket: 'some-bucket',
        CopySource: 'some-bucket/some/path/myfile.json,
        Key: 'some-bucket/some/other/path/myfile.json',
      })
      expect(mockS3Instance.copyObject).toHaveBeenCalledTimes(1)
   })
})

Some things to note

In the mockS3Instance I am using .mockReturnThis() . This is used to allow for chaining it to another method (in this case, promise which gets called like so: s3.copyObject(params).promise() in my code.

In the mockS3Instance , I have keys for promise and catch . This is because I was chaining copyObject with a promise() as well as a catch block, hence I’m mocking those out too.

Hope this helps someone! Thank you for reading.



Continue Learning