Mocking aws-sdk S3#putObject instance method using jest

vamsiampolu picture vamsiampolu · Mar 9, 2018 · Viewed 11.6k times · Source

For the source code containing:

 const S3 = require('aws-sdk/s3/clients')
 const s3 = new S3()
 s3.putObject(params, callback)

I have added the following mock based on this article:

 jest.mock('aws-sdk/s3/clients')
 const S3 = require('aws-sdk/s3/clients')

 it('has to mock S3#putObject', () => { 
    S3.prototype.putObject.mockImplementation(() => cb()) 
 })

But I cannot find the S3.prototype.putObject with / without mocking because the api seems to be built differently during an apiLoader pattern here. However, the definitions seem completely different here

I also tried with:

  const AWS = require('aws-sdk')
  console.log(AWS.S3.prototype.putObject) // undefined

How can I mock a method if I cannot find it on the prototype?

Wrapping in Promise breaks outcome

I have wrapped the source code in a Promise like this:

new Promise((resolve, reject) => {
    s3.putObject(params, (err, data) => {
        if (err) {
            reject(err)
        } else {
           resolve(data)
       }
    })
})

and using the test that looks like this:

const mockedPutObject = jest.fn();
jest.mock('aws-sdk/s3/clients', () => {
  return class S3 {
    putObject(params, cb) {
      mockedPutObject(params, cb);
    }
  }
});

it('should call aws S3.putObject method', async () => {
  const data = {
    Bucket: 'aaa',
    Key: 'bbb',
    Content: 'this can be anything',
    ACL: 'public-read'
  }
  await putObject(data)
  console.log(mockFn.calls)
  expect(mockFn).toBeCalledWith(data)
})

which results in an Error putObject › with appropriate params › should call aws S3.putObject method

Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

I think wrapping the calling code in a hand-rolled promise or using a library like this:

const {promisify} = require('es6-promisify')
const putS3Object = promisify(s3.putObject.bind(s3))
return putS3Object(data)

also fails similarly.

Answer

truchiranga picture truchiranga · Mar 9, 2018

For the source code

//src.js
const S3 = require('aws-sdk/clients/s3');
const s3 = new S3();

const putFunction = () => {
  s3.putObject(params, callback);
}
export default putFunction;

Following approach can be used to mock the putObject method of S3 client.

const mockedPutObject = jest.fn();
jest.mock('aws-sdk/clients/s3', () => {
  return class S3 {
    putObject(params, cb) {
      mockedPutObject(params, cb);
    }
  }
});

it('has to mock S3#putObject', () => {
    const putFunc = require('./src).default.putFunc;
    putFunc();
    expect(mockedPutObject).toHaveBeenCalledWith(params, callback);
 })