Can't decide how to test this code - node.js

I am attempting to write a test for the following code using mocha, chai, chai as promised & sinon but I am not too familiar with testing and have reached a mental wall.
const PasswordResets = require('../../../models/password-resets');
const ResponseError = require('../../../error-handlers/response-error');
function updatePasswordReset(email, token, doc = null) {
return new Promise((resolve, reject) => {
// If reset token already exists set it as the token
if (doc !== null) {
doc.token = token;
}
var passwordReset = doc === null ? new PasswordResets({ email, token }) : doc;
passwordReset.save(function (err, document) {
if (err) {
return reject(new ResponseError(err.message));
}
resolve(document);
});
});
}
module.exports = updatePasswordReset;
Any help would be greatly appreciated!

A part of the test that you can do as below
const sinon = require('sinon');
const chai = require('chai');
const PasswordResets = require('...');
const updatePasswordReset = require('...');
const assert = chai.assert;
describe('test', function () {
const document = 'doc'; // we will pass `document` for `save` callback func
beforeEach(function() {
// we use `sinon.stub` and `yields` for `save` callback function
sinon.stub(PasswordResets.prototype, 'save').yields(null, document);
});
afterEach(function() {
sinon.restore();
})
it('resets password successfully', function() {
return updatePasswordReset('test#gmail.com', '1234', null)
.then(res => {
assert.deepEqual(res, document); // check if the response correct
assert(PasswordResets.prototype.save.calledOnce); // check if it is being called
})
});
});
Ref:
https://sinonjs.org/releases/v6.1.5/stubs/#stubyieldarg1-arg2-

Related

Mocking function to unit test Serverless Lambda

I am really struggling to understand unit testing within a Serverless Application. So I obviously have my handler, and I have a single Lambda function
const responses = require('../utils/jsonResponse');
const someConnector = require('../services/connectToService/connectToService');
module.exports = async (event) => {
const connectionParams = {
//some env variables
};
try {
const token = await someConnector.connectToService(connectionParams);
return responses.status(token, 200);
} catch (e) {
return responses.status(
`Issue connecting to service - ${e.message}`,
500,
);
}
};
So this Lambda function is pretty straight forward, gets some environment variables, and awaits a response from a service. It then returns the response.
So I have already done integration tests for this which is fine, but now I wanted to do a Unit test. I wanted to test this function in isolation, so essentially I want to mock connectToService to return my own responses.
So I came up with the following
require('dotenv').config();
const { expect } = require('chai');
const sinon = require('sinon');
let sandbox = require("sinon").createSandbox();
const LambdaTester = require('lambda-tester');
const handler = require('../../../handler');
const msConnector = require('../../../services/connectToService/connectToService');
describe('Testing handler', async (done) => {
describe('endpoint someEndpoint returns 200', () => {
it('Should resolve with 200', async () => {
before(() => {
sandbox = sinon.createSandbox();
sandbox.stub(msConnector, 'connectToService').resolves('some-token');
});
afterEach(() => {
sandbox.restore();
});
await LambdaTester(handler.someEndpoint)
.expectResult((result) => {
console.log(result);
expect(result.statusCode).to.equal(200);
});
});
});
done();
});
msConnector is the filename of the service, connectToService is the function name. What I want to do is not invoke this function, but return some-token when my Lambda calls it.
However, I have the console.log, and what I get from that is the real token, not some-token.
This tells me that the mocked function is really being called and executed and returning the real value.
So how can I mock this to make sure it returns some-token?
Thanks
Service function
const { DOMParser } = require('#xmldom/xmldom');
const axios = require('axios');
const { loginRequest } = require('./xml/login');
const connectToService = async (connectionParams) => {
//this injects config details into XML
const xmlRequest = loginRequest(
connectionParams.username,
connectionParams.password,
connectionParams.url,
);
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': xmlRequest.length,
},
};
const token = await axios
.post(connectionParams.msHost, xmlRequest, config)
.then((res) => {
const dom = new DOMParser().parseFromString(res.data, 'text/xml');
if (
dom.documentElement
.getElementsByTagName('wsse:secToken')
.item(0)
) {
return dom.documentElement
.getElementsByTagName('wsse:secToken')
.item(0).firstChild.nodeValue;
}
throw new Error('Invalid Username/Password');
})
.catch((err) => {
throw new Error(`Error making connection - ${err.message}`);
});
return token;
};
module.exports = {
connectToService,
};
The function connectToService may be not same copy between you mocked and called.
Because you overwrote a new object by module.exports = .... This causes you probably get different object for each require.
Try to do the below approach sharing the same object for all require.
const { DOMParser } = require('#xmldom/xmldom');
const axios = require('axios');
const { loginRequest } = require('./xml/login');
const connectToService = async (connectionParams) => {
//this injects config details into XML
const xmlRequest = loginRequest(
connectionParams.username,
connectionParams.password,
connectionParams.url,
);
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': xmlRequest.length,
},
};
const token = await axios
.post(connectionParams.msHost, xmlRequest, config)
.then((res) => {
const dom = new DOMParser().parseFromString(res.data, 'text/xml');
if (
dom.documentElement
.getElementsByTagName('wsse:secToken')
.item(0)
) {
return dom.documentElement
.getElementsByTagName('wsse:secToken')
.item(0).firstChild.nodeValue;
}
throw new Error('Invalid Username/Password');
})
.catch((err) => {
throw new Error(`Error making connection - ${err.message}`);
});
return token;
};
module.exports.connectToService = connectToService;

Mocha Tests Running Before Docs Defined in Before Block Are Done

I am creating some mocha tests for my Node app. In my test, before retrieving some docs that are created, I need to first create those docs in the database. Then I retrieve them and run some tests on the results.
The problem I'm noticing is that even though I've included the function that needs to run to create the docs in the first before() block, and even though I'm awaiting the result of the doc creation function, my tests run BEFORE the docs are finished being created. It seems the before() block doesn't do quite what I think it does.
How can I rectify this to ensure the doc creation has finished BEFORE the test checks run?
const seedJobs = require('./seeder').seedJobs;
const MongoClient = require('mongodb').MongoClient;
const client = new MongoClient(`${url}${dbName}${auth}`);
describe("Seeding Script", async function () {
const testDate = new Date(2019, 01, 01);
let db;
before(async function () {
await seedJobs(); // This is the function that creates the docs in the db
return new Promise((resolve, reject) => {
client.connect(async function (err) {
assert.equal(null, err);
if (err) return reject(err);
try {
db = await client.db(dbName);
} catch (error) {
return reject(error);
}
return resolve(db);
});
});
});
// Now I retrieve the created doc and run checks on it
describe("Check VBR Code Update", async function () {
let result;
const jobName = 'VBR Code Update';
this.timeout(2000);
before(async function () {
result = await db.collection(collection).findOne({
name: jobName
});
});
it("should have a property 'name'", async function () {
expect(result).to.have.property("name");
});
it("should have a 'name' of 'VBR Code Update'", async function ()
expect(result.name).to.equal(jobName);
});
it("should have a property 'nextRunAt'", function () {
expect(result).to.have.property("nextRunAt");
});
it("should return a date for the 'nextRunAt' property", function () {
assert.typeOf(result.nextRunAt, "date");
});
it("should 'nextRunAt' to be a date after test date", function () {
expect(result.nextRunAt).to.afterDate(testDate);
});
});
// Other tests
});
You are mixing promises and async together which is not needed. The Nodejs driver supports async/await so rather keep it consistent.
I cannot see the seedJobs function but assume it works as expected. I suggest you update the before function as per the example below.
You also have an error initializing the date, the format should be:
const testDate = new Date(2019, 1, 1);
See the below init of mongodb client and use of await:
const mongodb = require('mongodb');
const chai = require('chai');
const expect = chai.expect;
const config = {
db: {
url: 'mongodb://localhost:27017',
database: 'showcase'
}
};
describe("Seeding Script", function () {
const testDate = new Date(2019, 1, 1);
let db;
seedJobs = async () => {
const collections = await db.collections();
if (collections.map(c => c.s.namespace.collection).includes('tests')) {
await db.collection('tests').drop();
}
let bulk = db.collection('tests').initializeUnorderedBulkOp();
const count = 5000000;
for (let i = 0; i < count; i++) {
bulk.insert( { name: `name ${i}`} );
}
let result = await bulk.execute();
expect(result).to.have.property("nInserted").and.to.eq(count);
result = await db.collection('tests').insertOne({
name: 'VBR Code Update'
});
expect(result).to.have.property("insertedCount").and.to.eq(1);
};
before(async function () {
this.timeout(60000);
const connection = await mongodb.MongoClient.connect(config.db.url, {useNewUrlParser: true, useUnifiedTopology: true});
db = connection.db(config.db.database);
await seedJobs();
});
// Now I retrieve the created doc and run checks on it
describe("Check VBR Code Update", async function () {
let result;
const jobName = 'VBR Code Update';
this.timeout(2000);
before(async function () {
result = await db.collection('tests').findOne({
name: jobName
});
});
it("should have a property 'name'", async function () {
expect(result).to.have.property("name");
});
});
});

Can't return data from callback function at NodeJS

When i create new variable and assign callback function, But data cannot return from callback function. Undefined is occurring at new variable.
const nedb = require('nedb');
const user = new nedb({ filename: './builds/database/users.db', autoload: true });
var s = user.find({}, function (err,docs) {
if(docs.length == 0) {
var data = false;
} else {
var data = docs;
}
return data;
});
console.log(s);
var s is undefined! ....
You are mixing up callback and Promise which are two different way to handle asynchronous calls.
I recommend you to use of Promises because they are simpler and the present and future of javascript.
Using async/await which is the next step after Promises
const user = {
find: () => ['jon', 'kalessi', 'jorah'],
};
async function getUsers() {
return (await user.find({})) || [];
}
async function myJob() {
const users = await getUsers();
console.log(users);
// Put your next code here
}
myJob();
Full promise :
const user = {
find: () => new Promise((resolve) => resolve(['jon', 'kalessi', 'jorah'])),
};
user.find({})
.then((docs = []) => {
console.log(docs);
// Put you next code here, you can use of the variable docs
})
.catch((err) => {
console.log(err);
});
Full callback :
const user = {
find: (_, callback) => callback(false, ['jon', 'kalessi', 'jorah']),
};
user.find({}, (err, docs = []) => {
if (err) {
console.log(err);
} else {
console.log(docs);
// Put you next code here, you can use of the variable docs
}
});
I think user.find returning the promise. you can do like this.
const nedb = require('nedb');
const user = new nedb({ filename: './builds/database/users.db', autoload: true });
var s = user.find({}, function (err,docs) {
if(docs.length == 0) {
var data = false;
} else {
var data = docs;
}
return data;
});
Promise.all(s)
.then(result => {
console.log(result);
});
Otherwise you can also use await Like this:
async function abc(){
const nedb = require('nedb');
const user = new nedb({ filename: './builds/database/users.db', autoload: true });
var s = await user.find({}, function (err,docs) {
if(docs.length == 0) {
var data = false;
} else {
var data = docs;
}
return data;
});
}
because await worked with async thats why i put it into async function.

Mocking connection pool and test url param using sinon?

I am doing a RESTful api using node.js,
Unit Testing using mocha, sinon, chai.
DB using ibm DB2.
Problem:
I am trying to a unit test on the getNews method.
I know the correct way of doing a unit test is not to access the database. So I am trying not to access the database.
Q1:
However, how do I do a mock on pool.open(connString, function (err, db) to return a fake result, assuming that I created the json result.
Assuming that my newsId that I want to get is 999.
Q2:
How do it ensure that the newsId i passed in is 999 and not another value?
Codes:
These are files that are already running correctly.
newsRest.js:
module.exports = (app) => {
const controller = require('../controller/newsController');
app.route('news/:newsId').get(controller.getNews);
};
newsController.js:
'use strict';
//imports
var connectionString = require('../common/ibmdb2Pool').connectionString;
var connString = connectionString();
var initPool = require('../common/ibmdb2Pool').initPool;
var pool = initPool();
const query = require('../db/query');
/*
* Restful API starts here
* */
//get news by id
exports.getNews = (req, res) => {
//get from request
const newsId = req.params.newsId;
const params = [newsId];
pool.open(connString, function (err, db) {
if (err) {
return console.log(err);
}
db.query(query.sqlSelect, params, function (error, result, info) {
if (error) {
console.log(error);
return false;
}
res.status(200).json({
result: result
});
});
});
};
ibmdb2Pool.js
'use strict';
//imports
require('dotenv').config();
var Pool = require("ibm_db").Pool;
// access the environment variables for this environment
var connString = process.env.CONNSTRING;
//variables
const minPoolSize = 20;
const maxPoolSize = 100;
//return connection string from environment file.
exports.connectionString = function () {
return connString;
};
//init the db pool with a min and max size
exports.initPool = function () {
var pool = new Pool();
var ret = pool.init(minPoolSize, connString);
if(ret !== true) {
console.log("Cannot init pool. " + ret);
}
pool.setMaxPoolSize(maxPoolSize);
return pool;
};
Here is my Unit Test class:
newsController.spec.js
process.env.NODE_ENV = 'test';
var initPool = require('../common/ibmdb2Pool').initPool;
var pool = initPool();
const sinon = require('sinon');
const request = require('request');
const chai = require('chai');
const chaiHttp = require('chai-http');
const should = chai.should();
let server = require('../../../main.js');
chai.use(chaiHttp);
describe('news service', () => {
beforeEach(() => {
this.open = sinon.stub(pool, 'open');
});
});
afterEach(() => {
pool.open.restore();
});
it('should list a SINGLE news /news/newsId GET', (done) => {
chai.request(server)
.get('/news/999')
.end(function(err, res){
res.should.have.status(200);
res.should.be.json;
res.body.should.be.a('object');
res.body.result.should.include.keys(
'NEWS_ID', 'TITLE', 'NEWS_TYPE', 'DESCRIPTION', 'CREATED_BY', 'CREATED_DATE'
);
done();
})
});
});
You can use callsArgWith :
sinon.mock(pool, 'open').callsArgWith(
1, // argument position
null, // 1st callback argument
{ success: true } // 2d cb argument
);
Details in the sinon stub documentation

stub nodejs promise in chain to return error

I'm new to using promises in nodejs and also in testing them. I have managed to test the individual modules separately, but when it comes to testing the chain of promises, I am having some trouble. I tried following the examples found here and on the npm page for sinon-as-promised but don't seem to managed to control the flow and trigger the error in the first promise of the chain.
I am using mocha, chai and sinon for my tests with sinon-as-promised and chai-as-promised.
I am trying to test this module:
'use strict';
var mySQS = require('./modules/sqs/sqs-manager');
var sWebHook = require('./modules/webhooks/shopify/webhooks');
var main = {};
main.manageShopifyWebhook = function (params, callback) {
sWebHook.verify(params.srcHmac, params.rawBody, params.shopName.split('.myshopify.com')[0], params.productId)
.then(function(data) {
var body = {
"params": {
"productId": data.productId,
"shopName": data.shopName
},
"job": "call-update-item"
};
mySQS.create_Queue(body)
.then(mySQS.send_Message)
.then(function(result) {
callback(null, result);
})
.catch(function(error) {
callback(error, null);
});
});
};
module.exports = main;
This is the sWebHook module I want to trigger the reject callback in the main flow:
'use strict';
var crypto = require('crypto');
var nconf = require('../../../../config/nconfig');
var webHookManager = {};
webHookManager.verify = function (srcHmac, rawBody, shopName, productId) {
return new Promise(function (resolve, reject) {
rawBody = new Buffer(rawBody, 'base64');
var sharedSecret = nconf.get('SHOPIFY_CLIENT_SECRET');
var digest = crypto.createHmac('SHA256', sharedSecret).update(rawBody).digest('base64');
console.log('***** CALCULATED DIGEST *****');
console.log(digest);
console.log('***** HMAC FROM SHOPIFY *****');
console.log(srcHmac);
if (digest !== srcHmac) {
console.log('Hello');
var customError = new Error('Unauthorized: HMAC Not Verified');
reject(customError);
return false;
}
var newEvent = {
shopName: shopName,
productId: productId
};
console.log('!! WEBHOOK VERIFIED !!');
resolve(newEvent);
});
};
module.exports = webHookManager;
And these are my tests so far (which do not work):
'use strict';
var chai = require('chai');
var sinonChai = require("sinon-chai");
var expect = chai.expect;
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
var sinon = require('sinon');
chai.use(sinonChai);
var proxyquire = require('proxyquire').noCallThru();
var AWS = require('mock-aws');
describe('MAIN', function() {
require('sinon-as-promised');
var testedModule,
sWebHookStub,
sqsQueueStub,
sqsSendMsgStub,
callbackSpy,
fakeDataObj;
before(function() {
sWebHookStub = sinon.stub();
sqsQueueStub = sinon.stub();
sqsSendMsgStub = sinon.stub();
callbackSpy = sinon.spy();
fakeDataObj = {
srcHmac: '12345',
rawBody: 'helloworld',
shopName: 'mario-test.myshopify.com',
productId: '6789'
};
testedModule = proxyquire('../lib/main', {
'./modules/webhooks/shopify/webhooks': {
'verify': sWebHookStub
},
'./modules/sqs/sqs-manager': {
'create_Queue': sqsQueueStub,
'send_Message': sqsSendMsgStub
}
});
});
it('calling shopifyVeriWebhook returns an error', function() {
var fakeError = new Error('Error verifying webhook');
sWebHookStub.rejects(fakeError);
testedModule.manageShopifyWebhook(fakeDataObj, function() {
callbackSpy.apply(null, arguments);
});
expect(callbackSpy).has.been.called.and.calledWith(fakeError, null);
});
});
So, I ended up figuring out how to test chains of promises using sinon. For the following main module (Note: the other modules all return promises):
'use strict';
var mySQS = require('./modules/sqs/sqs-manager');
var sWebHook = require('./modules/webhooks/shopify/webhooks');
var main = {};
//#params {object} params
//#params {string} params.srcHmac
//#params {string} params.rawBody
//#params {string} params.shopName - <shop-name.myshopify.com>
//#params {string} params.productId
main.manageShopifyWebhook = function (params) {
return new Promise(function(resolve, reject) {
sWebHook.verify(params.srcHmac, params.rawBody, params.shopName.split('.myshopify.com')[0], params.productId)
.then(function(data) {
var body = {
"params": {
"productId": data.productId,
"shopName": data.shopName
},
"job": "call-update-item"
};
return mySQS.create_Queue(body);
})
.then(mySQS.send_Message)
.then(resolve)
.catch(function(err) {
reject(err);
});
});
};
module.exports = main;
The secret is to manually resolve or reject the promises and write the expectation within the callback functions of the then or catch methods (just as we would do if we were writing tests for async code using done). And we then trigger the method we want to test, saving its value to a variable. Like so:
'use strict';
var chai = require('chai');
var sinonChai = require("sinon-chai");
var expect = chai.expect;
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
require('sinon-as-promised');
var sinon = require('sinon');
chai.use(sinonChai);
var proxyquire = require('proxyquire').noCallThru();
describe('MAIN', function() {
require('sinon-as-promised');
var testedModule,
sWebHookStub,
sqsQueueStub,
sqsSendMsgStub,
callbackSpy,
fakeDataObj;
before(function() {
sWebHookStub = sinon.stub();
sqsQueueStub = sinon.stub();
sqsSendMsgStub = sinon.stub();
callbackSpy = sinon.spy();
fakeDataObj = {
srcHmac: '12345',
rawBody: 'helloworld',
shopName: 'mario-test.myshopify.com',
productId: '6789'
};
testedModule = proxyquire('../lib/main', {
'./modules/webhooks/shopify/webhooks': {
'verify': sWebHookStub
},
'./modules/sqs/sqs-manager': {
'create_Queue': sqsQueueStub,
'send_Message': sqsSendMsgStub
}
});
});
it('calling shopifyVeriWebhook returns an error when trying to VERIFY WEBHOOK', function() {
var fakeError = new Error('Error verifying webhook');
sWebHookStub.rejects(fakeError)().catch(function(error) {
expect(shopifyWebhook).to.eventually.equal(error);
});
var shopifyWebhook = testedModule.manageShopifyWebhook(fakeDataObj);
});
it('calling shopifyVeriWebhook returns an error when trying to CREATE SQS QUEUE', function() {
var fakeBody = {
"params": {
"productId": '1234',
"shopName": 'name'
},
"job": "call-update-item"
};
var fakeError = new Error('Error creating sqs queue');
sWebHookStub.resolves(fakeBody)().then(function(result) {
sqsQueueStub.rejects(fakeError)().catch(function(error) {
expect(shopifyWebhook).to.eventually.equal(error);
});
});
var shopifyWebhook = testedModule.manageShopifyWebhook(fakeDataObj);
});
it('calling shopifyVeriWebhook returns an error when trying to SEND SQS MESSAGE', function() {
var fakeData = {
queueUrl: '5678',
payLoad: '{"message": "Hello World"'
};
var fakeBody = {
"params": {
"productId": '1234',
"shopName": 'name'
},
"job": "call-update-item"
};
var fakeError = new Error('Error sending sqs message');
sWebHookStub.resolves(fakeBody)().then(function(result) {
sqsQueueStub.resolves(fakeData)().then(function(result) {
sqsSendMsgStub.rejects(fakeError)().catch(function(error) {
expect(shopifyWebhook).to.eventually.equal(error);
});
});
});
var shopifyWebhook = testedModule.manageShopifyWebhook(fakeDataObj);
});
it('calling shopifyVeriWebhook is SUCCESSFUL', function() {
var fakeData = {
queueUrl: '5678',
payLoad: '{"message": "Hello World"'
};
var fakeBody = {
"params": {
"productId": '1234',
"shopName": 'name'
},
"job": "call-update-item"
};
var fakeResponse = {
'message': 'success'
};
sWebHookStub.resolves(fakeBody)().then(function(result) {
sqsQueueStub.resolves(fakeData)().then(function(result) {
sqsSendMsgStub.resolves(fakeResponse)().then(function(result) {
expect(shopifyWebhook).to.eventually.equal(result);
});
});
});
var shopifyWebhook = testedModule.manageShopifyWebhook(fakeDataObj);
});
});
Bonus sample - I needed to run my code on aws lambda, and therefore needed to have a final callback. So I had the main entry point to my code in a file called lambda.js:
'use strict';
var main = require('./lib/main');
//Verifies shopify webhooks
//#params {object} event
//#params {string} event.srcHmac
//#params {string} event.rawBody
//#params {string} event.shopName - <shop-name.myshopify.com>
//#params {string} event.productId
exports.shopifyVerifyWebHook = function (event, context, callback) {
console.log('---- EVENT ----');
console.log(event);
main.manageShopifyWebhook(event)
.then(function(result) {
callback(null, result);
})
.catch(function(err) {
callback(err, null);
});
};
And for this I needed to control the result of the promises and make sure the callback was called with either an error or a success message.
The premiss is the same.
describe('LAMBDA', function() {
var testedModule,
mainShopStub,
callbackSpy,
mainModule,
fakeEvent;
before(function() {
callbackSpy = sinon.spy();
fakeEvent = {
srcHmac: '12345',
rawBody: 'helloworld',
shopName: 'mario-test.myshopify.com',
productId: '6789'
};
testedModule = require('../lambda');
mainModule = require('../lib/main');
mainShopStub = sinon.stub(mainModule, 'manageShopifyWebhook');
});
after(function() {
mainShopStub.restore();
});
it('calling shopifyVerifyWebHook returns an error', function() {
var fakeError = new Error('Error running lambda');
mainShopStub.rejects(fakeError);
mainShopStub().catch(function (error) {
expect(callbackSpy).has.been.called.and.calledWith(error, null);
});
testedModule.shopifyVerifyWebHook(fakeEvent, {}, function() {
callbackSpy.apply(null, arguments);
});
});
it('calling shopifyVerifyWebHook return a data object', function() {
var fakeObj = {message: 'success'};
mainShopStub.resolves(fakeObj);
mainShopStub().then(function (result) {
expect(callbackSpy).has.been.called.and.calledWith(null, result);
});
testedModule.shopifyVerifyWebHook(fakeEvent, {}, function() {
expected.resolves(fakeObj);
callbackSpy.apply(null, arguments);
});
});
});
Before getting into how to test multiple promises and validating errors, there is a much larger problem with your code.
manageShopifyWebhook() is constructed using an anti-pattern of promises which is, you're using a callback structure to return your promise value instead of returning your promise directly. If you do this, you're taking away a large benefit of promises, direct chain for error handling. Furthermore, you won't be able to use sinon-as-promised and chai-as-promised since they expect a Promise/thenable to be returned.
However, this is a rather quick fix in your code, by simply returning the promise created by sWebHook.verify():
main.manageShopifyWebhook = function (params) {
// Return the promise directly
// the final return will be returned to the original caller of manageShopifyWebhook
return sWebHook.verify(params.srcHmac, params.rawBody, params.shopName.split('.myshopify.com')[0], params.productId)
.then(function(data) {
var body = {
"params": {
"productId": data.productId,
"shopName": data.shopName
},
"job": "call-update-item"
};
return mySQS.create_Queue(body);
})
.then(mySQS.send_Message)
.then(function(result) {
return result;
})
.catch(function(err) {
// In reality you can let error propagate out here
// if you don't need to do anything special with it and let
// the promise just return the error directly
// I've only done this so we can return 'Error Verifying Webhook' as an error from the promise returned by manageShopifyWebhook()
return Promise.reject(new Error('Error verifying webook'));
});
});
};
Now that manageShopfiyWebhook() is returning a promise, you can use the two as-promised test libraries.
For chai-as-promised you need to convert your expect() to look for a promise using the chain eventually and then you can use rejectedWith() to validate the Error/Error Message.
To validate multiple promises tests you can use Promise.all() and pass in all your promise returning assertions and return the outcome of Promise.all() to your mocha it().
I don't use sinon but the above should've given you enough direction to figure out how to use this pattern with sinon-as-promised as well since it will work for any Promise returning testing library.
it('calling shopifyVeriWebhook returns an error', function() {
var fakeError = new Error('Error verifying webhook');
let shopifyWebhook = testedModule.manageShopifyWebhook(fakeDataObj);
return Promise.all([
expect(shopifyWebhook).to.eventually.be.rejectedWith(fakeError);
]);
});

Resources