9

I have a function that looks like this:

function connect() {
   const secret = 'secret';
   const key = 'key';
   const region = 'region';
   const client = new AWS.DynamoDB({
      secret,
      key,
      region
   });'
   return new AWS.DynamoDB.DocumentClient({ service: client })
}

I would like to test the function connect. I have mocked the DynamoDB constructor like this:

// See https://stackoverflow.com/questions/47606545/mock-a-dependencys-constructor-jest
jest.mock('aws-sdk', () => {
  const DynamoDB = jest.fn().mockImplementation(() => {
    return {};
  });
  return {
    DynamoDB,
  };
});

However, this means that the DocumentClient constructor fails. How do I mock that as well?

skyboyer
  • 22,209
  • 7
  • 57
  • 64
vamsiampolu
  • 6,328
  • 19
  • 82
  • 183

7 Answers7

10

Building on the comment from duxtinto above:

In my case (and in the case of the OP if I'm reading it right), DynamoDB isn't being called as a function, but rather is an object with a DocumentClient field on it, so this worked for me:

jest.mock('aws-sdk', () => {
  return {
    DynamoDB: { // just an object, not a function
      DocumentClient: jest.fn(() => ({
        put: mockDynamoDbPut
      }))
    }
  }});
dpmott
  • 282
  • 3
  • 8
7

This worked for me:

const mockDynamoDbPut = jest.fn().mockImplementation(() => {
  return {
    promise() {
      return Promise.resolve({});
    }
  };
});

jest.doMock('aws-sdk', () => {
  return {
    DynamoDB: jest.fn(() => ({
      DocumentClient: jest.fn(() => ({
        put: mockDynamoDbPut
      }))
    }))
  };
});

I hope it's helpful for you too.

Regards,

David.

duxtinto
  • 379
  • 4
  • 6
  • 3
    This gives me the error `ConfigError: Missing region in config`. Any ideas would be appreciated! – Conan Oct 27 '20 at 21:20
  • When that happens to me it is usually related to the mock not working somehow or in the wrong place. That means DocumentClient tries to connect to db instance. – Guilherme Ribeiro Developer Nov 26 '20 at 23:24
  • 2
    In my case (and in the case of the OP if I'm reading it right), DynamoDB isn't being called as a function, but rather is an object wiht a DocumentClient field on it, so this worked for me: ` jest.mock('aws-sdk', () => { return { DynamoDB: { // just an object, not a function DocumentClient: jest.fn(() => ({ put: mockDynamoDbPut })) } }}); ` – dpmott Jun 07 '21 at 18:31
5

DocumentClient might call some of the client method, so simply define those methods stub. Say for example, DocumentClient would use batchGetItem, in your code

import AWS from 'aws-sdk';

jest.mock('aws-sdk', () => {
  const DynamoDB = jest.fn().mockImplementation(() => {
    return {
      batchGetItem: jest.fn(),
    };
  });
  return {
    DynamoDB,
  };
});

// you could inspect the mock
console.log(new AWS.DynamoDB({ ... }));
seanplwong
  • 1,051
  • 5
  • 14
1

Here what worked for me using jest with TypeScript:

// blabla.test.ts
import { DynamoDB } from 'aws-sdk';
import { ConsumerClass } from '../consumer-class';
import { DependencyConsumerClass } from '../dependency-consumer-class';

/*
* Inside consumerClassInstance.save() is calling this.dynamo.putItem({...}).promise();
*/
jest.mock('aws-sdk', () => {
    return {
        DynamoDB: jest.fn(() => {
            return {
                putItem: jest.fn(() => {
                    return {
                        promise: jest.fn(() => true)
                    };
                })
            };
        })
    };
});

test('sample test', async () => {
    const dependencyConsumerClass = new DependencyConsumerClass();
    const consumerClassInstance = new ConsumerClass(dependencyConsumerClass, new DynamoDB());
    
    const result = await consumerClassInstance.save();
    console.log(result);
});
1

Trying out the most voted answer, I tried to mock the document client and it didn't work.

I got this error: TypeError: AWS.DynamoDB.DocumentClient is not a constructor

Instead, I realized that I had to mock the DocumentClient as a class, not a function.

So, with that in mind, this is what worked instead:

jest.mock(`aws-sdk`, () => {
    class mockDocumentClient {
        async put(config) {
            //TODO: your logic here
            return true;
        }
        async query(config) {
            // TODO: your logic here
            return true;
        }
    }
    return {
        DynamoDB: {
            DocumentClient: mockDocumentClient,
    }};
});
Munib
  • 957
  • 1
  • 14
  • 30
0

Jest provides DynamoDB integration for running tests. See this document, look at 3. Configure DynamoDB client:

const {DocumentClient} = require('aws-sdk/clients/dynamodb');

const isTest = process.env.JEST_WORKER_ID;
const config = {
  convertEmptyValues: true,
  ...(isTest && {endpoint: 'localhost:8000', sslEnabled: false, region: 'local-env'})
};

const ddb = new DocumentClient(config);

I guess you can abstract out (if you haven't already) the DynamoDB client configuration into its own module file and export that client so that it can be required elsewhere, and when the Jest tests run, the client gets configured to point the mock DynamoDB server/tables that you have set up as per the other steps of the Jest DynamoDB documentation.

Jose Quijada
  • 558
  • 6
  • 13
  • 1
    It's really good to know that we don't have to write our own helpers to run integration tests (like I've been doing all this time), I would prefer to not use local dynamo server for unit tests. I think we can just use jest.mock('aws-sdk') if using ts-jest because aws-sdk now ships with typescript definitions. – vamsiampolu Jun 16 '20 at 23:08
0

For me, what worked is the following:

import AWS from "aws-sdk"
...
jest.spyOn(AWS.DynamoDB, "DocumentClient").mockReturnValue(mockedDocumentClient);

Here mockedDocumentClient is my mock object that fakes the DocumentClient behaviour. I just include the methods I would use on it.