1

Jumping into Jest and unit testing lambda functions. I'm trying to mock the aws dynamodb-document client scan in my unit test. I'm getting back the actual scan from the real db though so i know something with my mock isn't working. This is my .test.js:

var AWS = require('aws-sdk-mock');
const sinon = require('sinon');
let index = require('./index.js');


beforeEach(()=> {
    AWS.mock('DynamoDB.DocumentClient', 'scan', function(params, callback) {
        callback(null, {test: "value"});
    })
  });

  afterEach(()=> {
      AWS.restore();
  });

    it( 'test invocation', async() => {
        console.log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
      const result = await index.handler({}, {}, (err, result) => {
          expect(result).toEqual({test: "value"});
  });
  });

My actual lambda function code looks like this:

'use strict';

var AWS = require("aws-sdk");
var DOC = require("dynamodb-doc");
AWS.config.update({region: "us-east-1"//,
    });
var docClient = new AWS.DynamoDB.DocumentClient();
var params = {
    TableName: "table-name",
    FilterExpression: "#PK = :cat_val",
    ExpressionAttributeNames: {"#PK": "PK",},
    ExpressionAttributeValues: { ":cat_val": "Value"}
};

exports.handler = (event, context, callback) => {
    var response; 
    
    docClient.scan(params, onScan);
    function onScan(err, data) {
        if (err) {
            response = {
                statusCode: 500,
                body: "Unable to complete scan.  Error JSON: " + JSON.stringify(err,null,2)
            }
        } else {
            data.Items.sort(compare);
            response = {
                statusCode: 200,
                body: data.Items
            }
        }

        callback(null, response);
        console.log(response);
    }
}

Any direction you guys can point me in. The test fails on the equal compare because it has the actual response from the Dynamo scan.

Thanks, Tim

skyboyer
  • 22,209
  • 7
  • 57
  • 64
Tim B
  • 215
  • 1
  • 2
  • 12

1 Answers1

0

So the issue is in your function, before it is called AWS sdk is getting initialised thus it is always going to create that object before the handlers execution.

You need to mock the AWS library away so then you isolate your testing to the function you are trying to inspect

const DocumentClient = require("aws-sdk/clients/dynamodb");
const documentClient = new DocumentClient({ region: process.env.AWS_REGION }); // Set the AWS_REGION on your lambda

exports.handler = (event, context, callback) => {
  var response; 
  var params = {
   TableName: "table-name",
   FilterExpression: "#PK = :cat_val",
   ExpressionAttributeNames: {"#PK": "PK",},
   ExpressionAttributeValues: { ":cat_val": "Value"}
  };

  documentClient.scan(params, onScan);
  function onScan(err, data) {
    if (err) {
        response = {
            statusCode: 500,
            body: "Unable to complete scan.  Error JSON: " + JSON.stringify(err,null,2)
        }
    } else {
        data.Items.sort(compare);
        response = {
            statusCode: 200,
            body: data.Items
        }
    }

    callback(null, response);
    console.log(response);
  }
}

Thus then you can do your test as this with a manual mock of the dynamoDB DocumentCient

const index = require('./index.js');
const mockDynamoDbScan = jest.fn();

jest.mock('aws-sdk/clients/dynamodb', () => ({
   DocumentClient: jest.fn().mockImplementation(() => ({
     scan: mockDynamoDbScan
   }))
}))

describe('executeGet', () => {
  beforeEach(() => {
    jest.resetAllMocks();
    process.env.TABLE_NAME = 'SomeTable';
    process.env.AWS_REGION = 'someRegion';
  });

  it('Returns an item', () => {
    // Assemble - get the left and right of together
    mockDynamoDbScan.mockReturnValue({ Items: [] });

    // Action - execute functions
   let errResult;
   let functionResult;
   index.handler({}, {}, (err, result) => {
      errResult = err
      functionResult = result
   })

    // Assert - just populated some potential cases you may want to use
    expect(functionResult).toBeDefined();
    expect(functionResult.statusCode).toBeDefined();
    expect(functionResult.Items.length).toEqual(0);
  }):
}));

I may well have got some of the code slightly wrong due to having for format it in here, but this should give you gist of what you need. If this needs updating then just let me know