dwyl / aws-sdk-mock

:rainbow: AWSomocks for Javascript/Node.js aws-sdk tested, documented & maintained. Contributions welcome!
Apache License 2.0
1.1k stars 109 forks source link

Trouble mocking using aws-sdk promises #101

Open nitrocode opened 6 years ago

nitrocode commented 6 years ago

Here's my code:

function getItem(params) { return dynamodb.getItem(params).promise(); // return dynamodb.getItem(params).promise().then(response => response.Item); }

module.exports = { // ... getItem, }


* pool.js - one of many that use dynamodb.js
```javascript
const dynamo = require('./dynamodb');

async function get(id) {
  const params = {
    TableName: 'table',
    Key: {
      id: { S: `${id}v10` },
    },
  };
  return dynamo.getItem(params).catch(console.error);
}

module.exports = {
  // ...
  get,
}

let pool; let dynamodb;

function getItem(params) { return dynamodb.getItem(params).promise(); }

function getItem2(params) { return dynamodb.getItem(params).promise().then(response => response.Item); }

describe('Test getItem', function () { it('should return what we put in', async function () { AWSMock.mock('DynamoDB', 'getItem', function(params, callback) { callback(null, { response: { Item: { data: { S: 'data' } } } }); });

pool = require('./pool');
dynamodb = AWS.DynamoDB();

const params = {
  TableName: 'mydynamo',
  Key: {
    id: { S: 'myid' },
  },
};

let record;

// works
record = await AWS.DynamoDB().getItem(params).promise();
console.log(JSON.stringify(record));

// works
record = await getItem(params);
console.log(JSON.stringify(record));

// doesn't work
record = await getItem2(params);
console.log(JSON.stringify(record));

// works
record = await pool.get('id');
console.log(JSON.stringify(record));

}); });



I can mock the `getItem` call within the `it` function and print out `record` but cannot get an outside function to work to print out `record2`. Any suggestions? What am I doing wrong?
hoegertn commented 6 years ago

You have to call AWSMock.mock BEFORE instantiating the client object. So you cannot do const dynamodb = new AWS.DynamoDB(); and then mock it afterwards.

nitrocode commented 6 years ago

Thanks, this worked for me. I edited the above post. Does my setup make sense from the dev side and the test side now? Is there a better way to restructure the code to make testing / mocking easier?

vanor89 commented 6 years ago

Im having the same issue, its giving me lambda.invoke(...).promise is not a function, im doing the .mock() in the beforeEach func and restoring it in the afterEach, like so:

AWS.mock('Lambda', 'invoke', (params, callback) => {
      const awsResponse = {
        LogResult:'some-data',
        Payload:'{\'contentType\':\'text/plain\',\'message\':\'some-other-data\',\'status\':200}',
        StatusCode:200
      };
      callback(null, awsResponse);
    })
lambda.invoke({
      FunctionName: functionName,
      InvocationType: 'RequestResponse',
      LogType: 'Tail',
      Payload: Buffer.from(JSON.stringify(params))
    }).promise();

Do I need to change the way I call the callback function or wrap it in a promise or something in those lines?

ryan-mars commented 6 years ago

@CarlosV89 did you ever find a resolution to this? I'm having the same problem.

ariefitriadin commented 5 years ago

Here an example that I used in using lambda invoke ,

I'm creating a function called callSync() where the callSync() function is actually invoke another lambda function:


 exports.callSync = async function(params) {
  const lambda = new AWS.lambda({
      region: 'ap-southeast-1';
  });

    const invokeParam = {
        FunctionName: params.serviceName,
        InvocationType: params.invokeType,
        Payload: params.lambdaPayload,
        Qualifier: params.alias
    };

   await lambda.invoke(invokeParam).promise()
             .then(res => res.StatusCode)
             .catch((error) => {
                 throw new Error(error.message);
             }); 

}

then in my unit-testing, I'm using ava js anyway


const test = require('ava');
const sinon = require('sinon');
const AWS = require('aws-sdk-mock');

const callSync = require('./callSync');

test.serial('callSync invoke succesfully', async function(t) {

    const invokeReplace = function () {
        return new Promise(((resolve) => {
            resolve({ StatusCode: 200 });
        }));
    };

    AWS.mock('Lambda', 'invoke', invokeReplace());

    await callSync('RequestResponse', 'test-service', 'updateTest', {}).then(() =>  t.pass('sukses invoke callSync'));

});

test.serial('callSync will throw error on failed invocation, async function(t) {

        const invokeReplace = function () {
            return new Promise(((resolve, reject) => {
                reject(new Error('it is somewhat error'));
            }));
        };    

        AWS.mock('Lambda', 'invoke', invokeReplace());

        await callSync('RequestResponse', 'test-service', 'updateTest', {}).catch((error) => {
                t.is(error.message, 'it is somewhat error');
         });

});

test.beforeEach('Initialize New Sandbox Before Each Test', (t) => {
    t.context.sandbox = sinon.sandbox.create().usingPromise(Promise.Promise);
});

test.afterEach.always(
    'Restore Sandbox and Configuration After Each Test',
    (t) => {
        t.context.sandbox.restore();
        AWS.restore();
    }
);
ktwbc commented 4 years ago

The simplest thing to do is just test if you've been passed a callback and if not return a promise from the mock.

AWSMock.mock('StepFunctions', 'listStateMachines', (p: ListStateMachinesInput, callback: Function) => {
      let response: ListStateMachinesOutput = {
        stateMachines: [
          {
            stateMachineArn: 'arn:aws:states:us-east-1:123456789012:stateMachine:my-state-machine',
            name: 'my-state-machine',
            type: 'STANDARD',
            creationDate: moment().toDate()
          },
          {
            stateMachineArn: 'arn:aws:states:us-east-1:123456789012:stateMachine:my-second-machine',
            name: 'my-second-machine',
            type: 'STANDARD',
            creationDate: moment().toDate()
          }
        ]
      };
      if (!callback) {
        return new Promise((resolve => {
          resolve(response);
        }));
      } else {
        return callback(null, response);
      }
    });