Doing a mocha and chai unit testing for node js that need to query from dynamodb - unit-testing

Hi I am new to AWS dynamdoDB and mocha chai unit testing.
I wanted to create a node js unit testing with mocha and chai.
In my test.js i need to get expected outcome from AWS dynamoDB. However i not sure how to do it.
in my test.js
var assert = require('chai').assert;
describle('querying items from dynamodb', function(){
it('find date in Month collection', function(){
//not sure how should i put my inputs in here.
});
})
Do you have any articles or resources that I should read on?

If you want to make actual calls to AWS DynamoDB, a simple way to do it would be the following (based on documentation found for DynamoDB and DynamoDB.DocumentClient):
const assert = require('chai').assert;
const AWS = require('aws-sdk');
describe('querying items from dynamodb', function(){
it('find date in Month collection', function(done){
var params = {
TableName : <TEST_TABLE_NAME>,
Key: {
<PRIMARY_KEY>: <TEST_KEY_VALUE>
}
};
var expectedDate = <EXPECTED_VALUE>;
var documentClient = new AWS.DynamoDB.DocumentClient({apiVersion: '2012-08-10'});
documentClient.get(params, function(err, data) {
assert.strictEqual(data.Item.Date, expectedDate);
done();
});
});
});
BUT BUYER BEWARE! This will make calls to your actual DynamoDB and AWS may charge you money! To avoid this, mocking is highly recommended. Mocking calls to your DynamoDB can be done with the following code (based on documentation found on github, npmjs.com, and npmdoc.github.io):
const assert = require('chai').assert;
const AWS = require('aws-sdk');
const MOCK = require('aws-sdk-mock');
describe('querying items from dynamodb', function(){
before(() => {
// set up a mock call to DynamoDB
MOCK.mock('DynamoDB.DocumentClient', 'get', (params, callback) => {
console.log('Let us not call AWS DynamoDB and say we did.');
// return fake data
let fakeData = {
Item: {
Date: <FAKE_DATE>
}
};
return callback(null, fakeData);
});
});
after(() => {
// restore normal function
MOCK.restore('DynamoDB.DocumentClient');
});
it('find date in Month collection', function(done){
// set up the call like it's real
var params = {
TableName : <TEST_TABLE_NAME>,
Key: {
<PRIMARY_KEY>: <TEST_KEY_VALUE>
}
};
var expectedDate = <EXPECTED_VALUE>;
var documentClient = new AWS.DynamoDB.DocumentClient({apiVersion: '2012-08-10'});
documentClient.get(params, function(err, data) {
// data should be the fake object that should match
assert.strictEqual(data.Item.Date, expectedDate);
done();
});
});
});

Related

Promise.all(promiseList) is not working but Promise.all(promiseList[0]) is

I am using aws lambda that gets a list of items and it pushes items in the table asynchronously. Here promise.all(promiseList) does not add the data in the table but doing promise.all(promiseList[0]) ensures that my first element is pushed in the table. How can I make it to wait for all?
My code-
let promiseList = [];
// await Promise.all(
promiseList.push(
event.Records.map(async record => {
console.log(record.dynamodb.Keys.ServiceOrderId.S);
console.log('Inside initiate payment');
const paymentObject = {
paymentId: record.dynamodb.Keys.ServiceOrderId.S,
totalAmount: "1200"
};
console.log(paymentObject);
const dynamoDBParams = {
TableName : TABLE_NAME,
Item : paymentObject
};
return await DynamoDBClient.put(dynamoDBParams).promise();
}
)
);
return await Promise.all(promiseList[0]);
couple of changes I can see
We don't need to await for every put to complete, it will be inefficient, we want continue executing all at once.
DynamoDBClient.put(dynamoDBParams).promise() is returning a promise, which can be pushed into an array, rather than pushing entire loop
Here is how full Lambda looks like:
const AWS = require("aws-sdk");
AWS.config.update({ region: "us-east-1" });
let docClient = new AWS.DynamoDB.DocumentClient();
exports.handler = async function(event) {
const table = 'test'
var promiseArray = [];
event.Records.map(record => {
console.log('record', record);
const promise = docClient.put({
TableName: table,
Item: {
pk: record.pk,
title: record.title,
},
}).promise();
promiseArray.push(promise)
})
return Promise.all(promiseArray);
}

Error:: ResourceNotFoundException: Requested resource not found within AWS.config.credentials.refresh

I am trying to perform a dynamodb db operation and generate a report which worked fine but some times in production, am seeing the error - CredentialsError: Missing credentials in config, if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1 using identity poolid. Hence I updated the code to refresh the token as below -
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.789.0.min.js"></script>
var GetWallboardx = () => {
AWS.config.region = 'eu-west-2';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'eu-west-2:xxxxxxxxxxxxxxxxx0e8',
});
//refreshes credentials using AWS.CognitoIdentity.getCredentialsForIdentity()
AWS.config.credentials.refresh(error => {
if (error) {
console.error(error);
} else {;
var docClient = new AWS.DynamoDB.DocumentClient();
}
});
docClient.scan(params, function(err, data) {
-- some db operation --
})
setTimeout(GetWallboardx,RefreshInterval)// run this every 5 seconds
}
GetWallboardx()
if I try to use this 'refresh approach' looks like am not able to access dynamo db as the error says - Error:: ResourceNotFoundException: Requested resource not found. Can any one please help me. I did not get this exception when I did not use the 'refresh' earlier like this -
var docClient;
AWS.config.region = 'eu-west-2';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'eu-west-2:xxxxxxxxxxxxxxxxx0e8',
});
docClient = new AWS.DynamoDB.DocumentClient();
Because of refresh() is promise method that means it is running asynchronize. You may need some knowledge of async promise on javascript. Another thing you should refer to this link how to work with credentials refresh()
Users should call get / getPromise before using refresh
See example below to solve your case :
var GetWallboardx = async () => {
try {
// whether the credentials object should call refresh()
if (AWS.config.credentials.needRefresh()) {
await refreshCredentials();
}
let data = await docClient.scan(params).promise();
// some db operation
setTimeout(GetWallboardx(),5000) // run this every 5 seconds
} catch(e) {
// Error handler
}
}
var refreshCredentials = async () => {
await AWS.config.credentials.refreshPromise();
}
var getCredentials = async () => {
try {
AWS.config.region = 'eu-west-2';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'eu-west-2:xxxxxxxxxxxxxxxxx0e8',
});
// Should call this method to have ability using refresh()
await AWS.config.credentials.getPromise();
// once users get the credential identity, it is able to do refresh()
console.log('getCredentials done: ',AWS.config.credentials.identityId)
// await refreshCredentials()
var docClient = new AWS.DynamoDB.DocumentClient();
GetWallboardx();
} catch(e) {
// Error handler
}
}
getCredentials()
I not yet tried that code, but I think it should work as expected. Just let me know if you have a problem with.

DynamoDB : Query always returning null

I am quite new on DynamoDB, I just create a table where I inserted some dummy rows just to test the read / write functions.
Here is my data.
I create a Lambda to access these data, via Partition Key ok Sort Key but I ended up with a null return every time :'(
Here is the code of my lambda :
const dynamodb = new AWS.DynamoDB.DocumentClient({apiVersion: '2012-08-10', region: 'eu-west-1'});
exports.handler = async (event,context,callback) => {
var params = {
TableName : "DontMissAPlaceTable",
KeyConditionExpression: "Partition_Key = :id",
ExpressionAttributeValues: {
":id": {"S" : "media_001"}
}
};
dynamodb.query(params, function(err, data) {
if (err) {
console.log(err, err.stack);
callback(null, {
statusCode: '500',
body: err
});
} else {
console.log(data)
callback(null, {
statusCode: '200',
body: data
});
}
})
};
I first thought it was lack of access to the table but it seems I have the right permissions.
I am quite sure it's a dummy problem but i can't figure it out ...
Thanks for help :D
Your Lambda function is async but your code uses callbacks. By the time the callback is reached, your function has already been terminated because it ran asychronously.
Rather than using the outdated, confusing callback approach, just stick with async/await. The AWS SDK for Node.js already provides a .promise() method which you can call on asynchronous methods that use a callback, making it seamless to await on them.
That said, change your code to:
const dynamodb = new AWS.DynamoDB.DocumentClient({apiVersion: '2012-08-10', region: 'eu-west-1'});
exports.handler = async (event) => {
const params = {
TableName : "DontMissAPlaceTable",
KeyConditionExpression: "Partition_Key = :id",
ExpressionAttributeValues: {
":id": "media_001"
}
};
return {
statusCode: 200,
body: JSON.stringify(await dynamodb.query(params).promise())
}
};
Since you are already using the DocumentClient API, you don't need to specify the types ("S", "N", etc) when querying.
Also, make sure that Partition_Key really is your partition key. You use the name you defined in your table, no HashKey nor PartitionKey, meaning if you called your Hash Key id, then id should be used.

Unable to do unit testing for an API in sequelize using Mocha and Chai

Hi I am new to unit testing. I am currently working on Mocha,Chai and Sequelize to make a TDD unit test for an API. But while running the unit test using Mocha, I get the following error:
msg: 'TypeError: Cannot read property \'firstName\' of undefined' } to not exist
The unit test I have written for my API is as follows:
describe('createUser', function () {
it('should create a User', function (done) {
var email_Id = "xyz#gmail.com";
var firstName = "xyx";
var values = JSON.stringify({
body: {
email_Id: email_Id,
firstName: firstName
}
});
user.createUser(values, function (err, response) {
expect(response).should.be.an(object);
expect(err).to.be.null;
});
done();
});
});
My API is as follows:
createUser: function (req, res) {
var param = req.body;
var firstName = param.firstName;
var email_Id = param.email_Id;
db.sequelize.sync().then(function () {
user.create(param).then(function (response) {
// return a Success Response in JSON format
}).catch(function (err) {
// return an Error Response in JSON format
});
});
}
}
Can Somebody help me as to why am I getting this error?

Stubbing the mongoose save method on a model

I would like to stub the save method available to Mongoose models. Here's a sample model:
/* model.js */
var mongoose = require('mongoose');
var userSchema = mongoose.Schema({
username: {
type: String,
required: true
}
});
var User = mongoose.model('User', userSchema);
module.exports = User;
I have some helper function that will call the save method.
/* utils.js */
var User = require('./model');
module.exports = function(req, res) {
var username = req.body.username;
var user = new User({ username: username });
user.save(function(err) {
if (err) return res.end();
return res.sendStatus(201);
});
};
I would like to check that user.save is called inside my helper function using a unit test.
/* test.js */
var mongoose = require('mongoose');
var createUser = require('./utils');
var userModel = require('./model');
it('should do what...', function(done) {
var req = { username: 'Andrew' };
var res = { sendStatus: sinon.stub() };
var saveStub = sinon.stub(mongoose.Model.prototype, 'save');
saveStub.yields(null);
createUser(req, res);
// because `save` is asynchronous, it has proven necessary to place the
// expectations inside a setTimeout to run in the next turn of the event loop
setTimeout(function() {
expect(saveStub.called).to.equal(true);
expect(res.sendStatus.called).to.equal(true);
done();
}, 0)
});
I discovered var saveStub = sinon.stub(mongoose.Model.prototype, 'save') from here.
All is fine unless I try to add something to my saveStub, e.g. with saveStub.yields(null). If I wanted to simulate an error being passed to the save callback with saveStub.yields('mock error'), I get this error:
TypeError: Attempted to wrap undefined property undefined as function
The stack trace is totally unhelpful.
The research I've done
I attempted to refactor my model to gain access to the underlying user model, as recommended here. That yielded the same error for me. Here was my code for that attempt:
/* in model.js... */
var UserSchema = mongoose.model('User');
User._model = new UserSchema();
/* in test.js... */
var saveStub = sinon.stub(userModel._model, 'save');
I found that this solution didn't work for me at all. Maybe this is because I'm setting up my user model in a different way?
I've also tried Mockery following this guide and this one, but that was way more setup than I thought should be necessary, and made me question the value of spending the time to isolate the db.
My impression is that it all has to do with the mysterious way mongoose implements save. I've read something about it using npm hooks, which makes the save method a slippery thing to stub.
I've also heard of mockgoose, though I haven't attempted that solution yet. Anyone had success with that strategy? [EDIT: turns out mockgoose provides an in-memory database for ease of setup/teardown, but it does not solve the issue of stubbing.]
Any insight on how to resolve this issue would be very appreciated.
Here's the final configuration I developed, which uses a combination of sinon and mockery:
// Dependencies
var expect = require('chai').expect;
var sinon = require('sinon');
var mockery = require('mockery');
var reloadStub = require('../../../spec/utils/reloadStub');
describe('UNIT: userController.js', function() {
var reportErrorStub;
var controller;
var userModel;
before(function() {
// mock the error reporter
mockery.enable({
warnOnReplace: false,
warnOnUnregistered: false,
useCleanCache: true
});
// load controller and model
controller = require('./userController');
userModel = require('./userModel');
});
after(function() {
// disable mock after tests complete
mockery.disable();
});
describe('#createUser', function() {
var req;
var res;
var status;
var end;
var json;
// Stub `#save` for all these tests
before(function() {
sinon.stub(userModel.prototype, 'save');
});
// Stub out req and res
beforeEach(function() {
req = {
body: {
username: 'Andrew',
userID: 1
}
};
status = sinon.stub();
end = sinon.stub();
json = sinon.stub();
res = { status: status.returns({ end: end, json: json }) };
});
// Reset call count after each test
afterEach(function() {
userModel.prototype.save.reset();
});
// Restore after all tests finish
after(function() {
userModel.prototype.save.restore();
});
it('should call `User.save`', function(done) {
controller.createUser(req, res);
/**
* Since Mongoose's `new` is asynchronous, run our expectations on the
* next cycle of the event loop.
*/
setTimeout(function() {
expect(userModel.prototype.save.callCount).to.equal(1);
done();
}, 0);
});
}
}
Have you tried:
sinon.stub(userModel.prototype, 'save')
Also, where is the helper function getting called in the test? It looks like you define the function as the utils module, but call it as a method of a controller object. I'm assuming this has nothing to do with that error message, but it did make it harder to figure out when and where the stub was getting called.