NodeJS: Mock promise of aws-sdk - node.js

I am currently working on some code in NodeJS.
My code looks like this:
const AWS = require('aws-sdk');
const PARAMS_ACCOUNT = {
AttributeNames: [
"max-instances"
]
};
const ec2 = new AWS.EC2();
const getAccountAttributes = ec2.describeAccountAttributes(PARAMS_ACCOUNT).promise();
function maxInstances() {
return getAccountAttributes.then(data => {
return data.AccountAttributes[0].AttributeValues[0].AttributeValue;
});
}
I've tried to use aws-sdk-mock, beside chaiJS and mochaJS but I'am not sure how to mock the .promise() of the aws-sdk.
My attempt looks like the following:
const chai = require('chai');
const mocha = require('mocha');
const awsSDK = require('aws-sdk');
const aws = require('aws-sdk-mock');
const server = require('../instance_limit.js');
const expect = chai.expect;
describe('Sample test', () => {
it('this is also a test', () => {
aws.mock('EC2','describeAccountAttributes',function (params, callback){
callback(null, {AccountAttributes:[{AttributeValues:[{AttributeValue: 10}]}]});
});
var awsEC2 = new awsSDK.EC2();
const instances = server.maxInstances();
expect(instances).to.equal(10);
});
});
But with this I am not able to mock the aws-sdk call.
Thanks for your help.

I was able to solve the problem by using the principle of dependency injection which prevents from mocking the aws sdk in this case, for that it was necessary to adjust my function a little bit and set a default value.
It now looks like this:
const AWS = require('aws-sdk');
const PARAMS_ACCOUNT = {
AttributeNames: [
"max-instances"
]
};
const ec2 = new AWS.EC2();
const getAccountAttributes = ec2.describeAccountAttributes(PARAMS_ACCOUNT).promise();
function maxInstances(accountAttributes = getAccountAttributes()) {
    return accountAttributes.then(data => {
        return data.AccountAttributes[0].AttributeValues[0].AttributeValue;
    });
}
This now allows me to provide a sample value during the tests, like this:
const chai = require('chai');
const mocha = require('mocha');
const server = require('../instance_limit.js');
const expect = chai.expect;
describe('data calculation based on ec2 values', () => {
it(' expects to return max Instances value equal 10', () => {
const accountAttributeObject = {AccountAttributes:[{AttributeValues:[{AttributeValue: 10}]}]}
const accountAttributes = Promise.resolve(accountAttributeObject);
const instances = server.maxInstances(accountAttributes);
return instances.then( data => {
expect(data).to.equal(10);
});
});
})

Related

sinon stub calls fake calling actual function

I'm having situation where I want to write unit test cases for a function to make sure if it is working fine or not. So I have created stub for that specific function and when I tries to calls fake that stub, the function is actually getting called instead of fake call. Below is my scenario:
I have an main function from where I'm calling the function saveData(**).
saveData(**) function is calling AWS SQS to save an message to DB
Below is my main function:
'use strict';
async function mainFunction() {
try {
await saveData(
name,
age,
);
return true;
} catch (e) {
console.log('Error - [%s]', e);
return null;
}
}
module.exports = { mainFunction };
Below is my saveData(**) function:
'use strict';
const AWS = require('aws-sdk');
const sqs = new AWS.SQS();
const saveData = async (
name,
age,
) => {
await sendMessage(JSON.stringify(dbData));
const params = {
DelaySeconds: <some_delay>,
MessageAttributes: <messageAttributes>,
MessageBody: {name:name, age:age},
QueueUrl: <URL_FOR_QUEUE>,
};
return sqs.sendMessage(params).promise();
return true;
};
module.exports = {
saveData,
};
And my test case is,
'use strict';
const express = require('express');
const bodyParser = require('body-parser');
require('app-module-path').addPath('./src');
const sinon = require('sinon');
const app = express();
const sqsSender = require('lib/queue');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const main = require('../../../src/main-function');
const routes = require('routes');
routes.configure(app);
let mainFunctionStub;
let saveDataStub;
describe('/v1/main', () => {
beforeEach(() => {
mainFunctionStub = sinon.stub(main, 'mainFunction');
saveDataStub = sinon.stub(sqsSender, 'saveData');
});
describe('Test', () => {
it(`should return success`, (done) => {
const name = 'Name';
const age = 'Age';
saveDataStub.resolves({
name,
age,
});
});
});
afterEach(() => {
mainFunctionStub.restore();
mainFunctionStub.reset();
saveDataStub.restore();
saveDataStub.reset();
});
});
But this test is returning,
error: Jun-20-2021 20:07:05: Error - [Error [ConfigError]: Missing region in config
and,
Error: Timeout of 3500ms exceeded.
From this error I can say that this is actually calling SQS function instead of faking. How can I resolve this or how can I fake call to this function? I'm new to this unit testing so any help would be appriciated.
Stubbing works by replacing the property on the exports object. Since the require happens before sinon replaces the function, you capture the reference to the original function instead of dynamically using the currently set one.
You haven't showed your require calls in the main file, but from the call-site I infer you're importing it like const { saveData } = require('../foo/sqsSender'). This means you're grabbing the function off of the object when first loading the code. If you instead keep a reference to the sqsSender module instead, and reference the function on invocation, the stub should work.
'use strict';
// Don't destructure / grab the function.
const sqsSender = require("../foo/sqsSender")+
async function mainFunction() {
try {
// Use reference through module
await sqsSender.saveData(
name,
age,
);
return true;
} catch (e) {
console.log('Error - [%s]', e);
return null;
}
}
module.exports = { mainFunction };

How to use cls-hooked unmanaged transactions?

I'm writing tests with jest and sequelize and I need to keep my database clean for every test, so I want to set a transaction for every test and then rollback at the end.
This is what I've got, but it wont pass the transaction to my tests:
beforeEach(async () => {
this.transaction = await db.sequelize.transaction();
});
test('Database should be clean', async () => {
const role = await db.role.create({
name: 'someName',
});
expect(role.id).toBe(1);
});
afterEach(async () => {
await this.transaction.rollback();
});
Sequelize is already setted to use cls
const Sequelize = require('sequelize');
const config = require('../../config/config.js');
const cls = require('cls-hooked');
const namespace = cls.createNamespace('testing-namespace');
Sequelize.useCLS(namespace);
const sequelize = new Sequelize(config);
...
It would be really helpful if somenone could explain me how to use unmanaged transactions with cls-hooked.
I managed to keep my database clean by using umzug to run seeds programatically, this is my code:
const { db } = require('../models');
const Umzug = require('umzug');
const path = require('path');
const umzug = new Umzug({
migrations: {
path: path.join(__dirname, './../../seeders'),
params: [db.sequelize.getQueryInterface()],
},
logging: false,
});
beforeEach(async () => {
const reverted = await umzug.down({ to: 0 });
const executed = await umzug.up();
});
I tried to use umzug v3 (beta), but it didn't work, so I used the stable version 2. This approach isn't as performant as I'd like, but gets the job done.

stubbing AWS Dynamodb documentClient for integration test

im having some issues stubbing this dependency. I know there ir a aws-sdk-mock modules but mi goal its to stub it with sinon and chai.
Here is mi code,
Test code
const chai = require('chai');
const sinon = require('sinon');
const chaiHttp = require('chai-http');
const app= require('./app');
chai.use(chaiHttp);
const queryMock =sinon.stub();
const dynamoMock = {
DocumentClient:sinon.fake.returns({
query:queryMock
})
}
let awsDynamoMock;
describe.only('Integration test for activation',()=>{
beforeEach(() => {
awsDynamoMock = sinon.stub(require('aws-sdk'),'DynamoDB');
awsDynamoMock.returns(dynamoMock);
})
afterEach(() => {
awsDynamoMock.restore();
})
it('Request /endpoint returns HTTP 200 with {} when user exist and all task are done',(done)=>{
const params = {
TableName:'table',
KeyConditionExpression: `client_id= :i`,
ExpressionAttributeValues: {
':i': '23424234'
},
ConsistentRead:true
};
const userWithNoPendingsMock = {
Items: [
{
client_id: "23424234",
},
],
Count: 1,
ScannedCount: 1,
}
queryMock.withArgs(params).returns({
promise:() =>sinon.fake.resolves(userWithNoPendingsMock)
})
chai
.request(app)
.post("/endpoint")
.end( (err, res) => {
chai.expect(res.status).to.be.eql(200);
chai.expect(res.body).to.eql({});
done();
});
});
})
Connection to dynamoDB to stub
const AWS = require('aws-sdk');
AWS.config.update({region:'REGION'});
let docClient = false;
const getDynamoSingleton = async () => {
if (docClient) return docClient;
docClient = new AWS.DynamoDB.DocumentClient();
console.log(docClient)
return docClient
}
module.exports = getDynamoSingleton
Using DynamoDB example
const getElementById = async (TableName,key,id)=>{
const docClient = await getDynamoSingleton();
//Make query params.
const params = {
TableName,
KeyConditionExpression: `${key} = :i`,
ExpressionAttributeValues: {
':i': id
},
ConsistentRead:true
};
//Run query as promise.
return docClient.query(params).promise();
}
Im really stuck on this problem, so any help would be useful. I know the problem has something to do with de documentclient
Thanks for the help
I realize this is an old question, but you can set up a resolvable object with a little trickery. Some inspiration from this answer.
const sandbox = require('sinon').createSandbox();
const AWS = require('aws-sdk');
describe('...', () => {
it('...',
 (done) => {
// Create a dummy resolver, which returns an empty object.
const dummy = {func: () => {}};
sandbox.stub(dummy, 'func').resolves({some: 'fake response'});
// Mock docClient.query. Binding to .prototype should make this apply to any `new AWS.DynamoDB.DocumentClient()` calls.
sandbox.stub(AWS.DynamoDB.DocumentClient.prototype, 'query').returns({promise: dummy.func});
// Run your tests here.
});
});
This is cut down to remove a lot of the extra configuration you are doing (and probably need). We create a dummy object with the function func which returns a sinon promise.
Next, we stub the AWS.DynamoDB.DocumentClient prototype so that new AWS.DynamoDB.DocumentClient() will receive our sinon stub.
Third, we configure our DocumentClient prototype stub to return a plain javascript object with a property called promise. This property points to the first dummy object's promise-returning func method.
Now calls to docClient.query(params).promise() should receive a mocked promise. docClient.query(params) will receive the stub sandbox.stub(AWS.DynamoDB.DocumentClient.prototype, ...). And .promise() will be processed from {promise: dummy.func} to refer to the dummy resolver.

To mock AWS SES with Sinon

I'm trying to mock SES with Sinon, but facing below error. Tried using aws-sdk-mock, but it's not working.
Error: TypeError: Cannot stub non-existent own property sendEmail
Code snippet of test class:
import * as AWS from 'aws-sdk';
const sandbox = sinon.createSandbox();
sandbox.stub(AWS.SES, 'sendEmail').returns({promise: () => true});
Actual class:
import * as AWS from 'aws-sdk';
import * as _ from 'lodash';
export async function sendAlertMailOnFailure(status:any)
{
// load AWS SES
var ses = new AWS.SES();
const params = {
Destination: {
ToAddresses: <to_address>
},
Message: {...},
Source: <sender_address>
}
ses.sendEmail(params, (err, data) => {
if (err) {
log.error("Error sending mail::");
log.error(err, err.stack);
}
})
}
Is there any way to mock SES with Sinon or with aws-sdk-mock?
My answer here is not a direct solution for SES, but it is a working solution I'm using for mocking DynamoDB.DocumentClient and SQS. Perhaps you can adapt my working example for SES and other aws-sdk clients in your unit tests.
I just spent hours trying to get AWS SQS mocking working, without resorting to the aws-sdk-mock requirement of importing aws-sdk clients inside a function.
The mocking for AWS.DynamoDB.DocumentClient was pretty easy, but the AWS.SQS mocking had me stumped until I came across the suggestion to use rewire.
My lambda moves bad messages to a SQS FailQueue (rather than letting the Lambda fail and return the message to the regular Queue for retries, and then DeadLetterQueue after maxRetries). The unit tests needed to mock the following SQS methods:
SQS.getQueueUrl
SQS.sendMessage
SQS.deleteMessage
I'll try to keep this example code as concise as I can while still including all the relevant parts:
Snippet of my AWS Lambda (index.js):
const AWS = require('aws-sdk');
AWS.config.update({region:'eu-west-1'});
const docClient = new AWS.DynamoDB.DocumentClient();
const sqs = new AWS.SQS({ apiVersion: '2012-11-05' });
// ...snip
Abridged Lambda event records (event.json)
{
"valid": {
"Records": [{
"messageId": "c292410d-3b27-49ae-8e1f-0eb155f0710b",
"receiptHandle": "AQEBz5JUoLYsn4dstTAxP7/IF9+T1S994n3FLkMvMmAh1Ut/Elpc0tbNZSaCPYDvP+mBBecVWmAM88SgW7iI8T65Blz3cXshP3keWzCgLCnmkwGvDHBYFVccm93yuMe0i5W02jX0s1LJuNVYI1aVtyz19IbzlVksp+z2RxAX6zMhcTy3VzusIZ6aDORW6yYppIYtKuB2G4Ftf8SE4XPzXo5RCdYirja1aMuh9DluEtSIW+lgDQcHbhIZeJx0eC09KQGJSF2uKk2BqTGvQrknw0EvjNEl6Jv56lWKyFT78K3TLBy2XdGFKQTsSALBNtlwFd8ZzcJoMaUFpbJVkzuLDST1y4nKQi7MK58JMsZ4ujZJnYvKFvgtc6YfWgsEuV0QSL9U5FradtXg4EnaBOnGVTFrbE18DoEuvUUiO7ZQPO9auS4=",
"body": "{ \"key1\": \"value 1\", \"key2\": \"value 2\", \"key3\": \"value 3\", \"key4\": \"value 4\", \"key5\": \"value 5\" }",
"attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1536763724607",
"SenderId": "AROAJAAXYIAN46PWMV46S:steve.goossens#bbc.co.uk",
"ApproximateFirstReceiveTimestamp": "1536763724618"
},
"messageAttributes": {},
"md5OfBody": "e5b16f3a468e6547785a3454cfb33293",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:eu-west-1:123456789012:sqs-queue-name",
"awsRegion": "eu-west-1"
}]
}
}
Abridged unit test file (test/index.test.js):
const AWS = require('aws-sdk');
const expect = require('chai').expect;
const LamdbaTester = require('lambda-tester');
const rewire = require('rewire');
const sinon = require('sinon');
const event = require('./event');
const lambda = rewire('../index');
let sinonSandbox;
function mockGoodSqsMove() {
const promiseStubSqs = sinonSandbox.stub().resolves({});
const sqsMock = {
getQueueUrl: () => ({ promise: sinonSandbox.stub().resolves({ QueueUrl: 'queue-url' }) }),
sendMessage: () => ({ promise: promiseStubSqs }),
deleteMessage: () => ({ promise: promiseStubSqs })
}
lambda.__set__('sqs', sqsMock);
}
describe('handler', function () {
beforeEach(() => {
sinonSandbox = sinon.createSandbox();
});
afterEach(() => {
sinonSandbox.restore();
});
describe('when SQS message is in dedupe cache', function () {
beforeEach(() => {
// mock SQS
mockGoodSqsMove();
// mock DynamoDBClient
const promiseStub = sinonSandbox.stub().resolves({'Item': 'something'});
sinonSandbox.stub(AWS.DynamoDB.DocumentClient.prototype, 'get').returns({ promise: promiseStub });
});
it('should return an error for a duplicate message', function () {
return LamdbaTester(lambda.handler)
.event(event.valid)
.expectReject((err, additional) => {
expect(err).to.have.property('message', 'Duplicate message: {"Item":"something"}');
});
});
});
});
You need to use prototype in AWS to stub it:
import AWS from 'aws-sdk';
const sandbox = sinon.createSandbox();
sandbox.stub(AWS.prototype, 'SES').returns({
sendEmail: () => {
return true;
}
});
The error seems to indicate that AWS is being imported as undefined.
It might be that your ES6 compiler isn't automatically turning this line:
import AWS from 'aws-sdk';
...into an import of everything in aws-sdk into AWS.
Change it to this:
import * as AWS from 'aws-sdk';
...and that may fix the issue.
(Disclaimer: I can't reproduce the error in my environment which is compiling with Babel v7 and automatically handles either approach)
Using require & without using prototype. This is working for me for mocking DynamoDB.
const aws = require('aws-sdk');
const sinon = require('sinon');
const sandbox = sinon.createSandbox();
this.awsStub = sandbox.stub(aws, 'DynamoDB').returns({
query: function() {
return {
promise: function() {
return {
Items: []
};
}
};
}
});
Packages:
"aws-sdk": "^2.453.0"
"sinon": "^7.3.2"
I was able to use awk-sdk-mock by doing the following:
test class
const AWSMock = require('aws-sdk-mock');
const AWS = require('aws-sdk');
AWSMock.setSDKInstance(AWS);
...
AWSMock.mock('SES', 'sendRawEmail', mockSendEmail);
// call method that needs to mock send an email goes below
sendEmail(to, from, subject, body, callback);
function mockSendEmail(params, callback) {
console.log('mock email');
return callback({
MessageId: '1234567',
});
}
Actual class
const aws = require('aws-sdk');
const nodemailer = require('nodemailer');
function sendEmail(to, from, subject, body, callback) {
let addresses = to;
if (!Array.isArray(addresses)) {
addresses = [addresses];
}
let replyTo = [];
if (from) {
replyTo.push(from);
}
let data = {
to: addresses,
replyTo,
subject,
text: body,
};
nodemailer.createTransport({ SES: new aws.SES({ apiVersion: '2010-12-01' }) }).sendMail(data, callback);
}
const AWS = require('aws-sdk');
...
const sandbox = sinon.createSandbox();
sandbox.stub(AWS, 'SES').returns({
sendRawEmail: () => {
console.log("My sendRawEmail");
return {
promise: function () {
return {
MessageId: '987654321'
};
}
};
}
});
let ses = new AWS.SES({ region: 'us-east-1' });
let result = ses.sendRawEmail(params).promise();

Sinon: Mocking websockets/ws

How can I mock up websockets/ws using Sinon? I'm trying to test that my application behaves as expected when using WebSockets, without necessarily needing to connect each time (eg: testing event handlers, etc).
Coming from a C# background, I'd just mock out the whole interface using a library like Moq, and then verify that my application had made the expected calls.
However, when trying to do this with Sinon, I'm running into errors.
An example of a test:
const WebSocket = require('ws');
const sinon = require('sinon');
const webSocket = sinon.mock(WebSocket);
webSocket.expects('on').withArgs(sinon.match.any, sinon.match.any);
const subject = new MyClass(logger, webSocket);
This class is then calling:
this._webSocket.on("open", () => {
this.onWebSocketOpen();
});
But when I try and run my tests, I get this error:
TypeError: Attempted to wrap undefined property on as function
What's the correct way to mock out an object like this using Sinon?
Thanks.
If your just trying to test if the given sockets 'on' method was called when passed in, this is how you would do it:
my-class/index.js
class MyClass {
constructor(socket) {
this._socket = socket;
this._socket.on('open', () => {
//whatever...
});
};
};
module.exports = MyClass;
my-class/test/test.js
const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');
const sinon_chai = require('sinon-chai');
const MyClass = require('../index.js');
const sb = sinon.sandbox.create();
chai.use(sinon_chai);
describe('MyClass', () => {
describe('.constructor(socket)', () => {
it('should call the .prototype.on method of the given socket\n \t' +
'passing \'open\' as first param and some function as second param', () => {
var socket = { on: (a,b) => {} };
var stub = sb.stub(socket, 'on').returns('whatever');
var inst = new MyClass(socket);
expect(stub.firstCall.args[0]).to.equal('open');
expect(typeof stub.firstCall.args[1] === 'function').to.equal(true);
});
});
});

Resources