Stubbing order and process in Unit Testing function - node.js

I am starting to improve at Unit testing, but still thrown off by a few things. So I have the following service function
const connectToService = async (connectionParams) => {
try {
const xmlRequest = await getLoginXml(
connectionParams.username,
connectionParams.password,
connectionParams.url,
);
const res = await axios.post(connectionParams.msHost, xmlRequest);
const dom = new DOMParser().parseFromString(res.data, 'text/xml');
if (
!dom.documentElement
.getElementsByTagName('wsse:BinarySecurityToken')
.item(0)
) {
throw new Error('Invalid Username/Password');
}
return dom.documentElement
.getElementsByTagName('wsse:BinarySecurityToken')
.item(0).firstChild.nodeValue;
} catch (e) {
throw new Error(`Error making connection - ${e.message}`);
}
};
module.exports = {
connectToService,
};
So I have a relatively straight forward function above. Takes connectionParams and injects some params using getLoginXml, which just returns some XML.
I then make a POST request with that xml and receive a response. I then parse this response and return a token if there is one, if not, I throw an Error.
The thing that is throwing me is that there are a few things going on in this function, and I am not sure what to stub.
SO what would be the process first? Stub getLoginXml and then let the rest of the function run? And go through the file sequentially?
Any advice appreciated - I have done well with my integration tests, but unit test I am struggling to fully get to grips with.
Thanks

Related

Returning a value from a mix of async/sync function from a provider (different script) to express server

Apologies for asking this question - I know there are tons of information about async functions out there but I seem to have tried everything and cannot find a solution..
First of all let me outline the architecture of my program. There are two scripts: a main server script (node.js, express), which processes GET requests and provider script, which deals with the blockchain in the background to return some values. The server script is responsible for invoking a method that returns a value from the provider. The provider does all the work.
The snippet of the provider script:
getInfo(index, account, key) {
//Waiting on an asynchronous method, which does some work in the blockchain in the background; everything functions as it should be
try {
await this.getBlockchain
(
index
, account
, key
).then(result => {
// Here instead I invoke a SYNCHRONOUS method, which simply formats the response in a correct way
const reply = this.sendReply(result)
console.log(reply) //Logs the correct reply in the format in which the server is expecting it
return reply;
});
}
catch (error) {
return { error: 003, result: false };
}
}
The snippet of the server script:
server.get("/getAccount", async (req, res) => {
let index = req.query.index;
let account = req.query.account;
let key = req.query.key;
// Here I also check for the validity of the query values, irrelevant to this issue
// The provider class is imported as provider, hence, the provider.method (this has been tested many times before)
try {
await provider.getInfo(index, account, key).then(reply => {
const { error: infoError, result: infoValue } = reply
if (infoError == false) {
res.send(`${infoValue}`);
} else {
res.send(`${infoError}`);
};
});
}
catch (error) {
res.send("008");
}
}
);
I honestly have no idea how to approach this; I tried self-contained async function on the server side as well as different syntax but the reply is always undefined even though the reply from a synchronous call in the provider is correct.
Could someone help me to understand what I'm doing wrong? This is my first time working with async with numerous scripts and functions and I'm finding it very confusing.
Thank you so much!
With your current structure, you need to return the result of the await so that the top level of your function is returning something from the async function.
async getInfo(index, account, key) {
try {
let retVal = await this.getBlockchain(index, account, key).then(result => {
return this.sendReply(result);
});
return retVal;
} catch (error) {
return { error: 003, result: false };
}
}
But, really, it's a better coding style to not mix await and .then() and to just go with one style like this:
async getInfo(index, account, key) {
try {
let result = await this.getBlockchain(index, account, key);
return this.sendReply(result);
} catch (error) {
return { error: 003, result: false };
}
}
Note, this function never rejects because it's catching its own rejections and turning it into a resolved value. So, the caller cannot use .catch() to see errors. The caller must always check for the error property in the resolved object. This is not usually how you program with promises. It can be made to work, but often does not meet the expectations of the caller (as errors are usually communicated back via rejected promises).
This has to be a dup. but... Don't mix await and .then.
You simply try/catch around await.
try {
const reply = await provider.getInfo(index, account, key);
const { error: infoError, result: infoValue } = reply
if (infoError == false) {
res.send(`${infoValue}`);
} else {
res.send(`${infoError}`);
};
} catch (error) {
res.send(500);
}

Throwing exceptions on AWS Lambda with Node12.x in Axios catch handlers

I'm having a hard time trying to understand how I can make this function work. I think it stems from me not really understanding 100% how to handle promises, but I've read all the tutorials I can find and I still don't see how I can do this.
The following is a function, which calls an endpoint on an API. When it's successful, it'll return a token for use in future requests. When it fails, I want to throw an exception and completely stop the execution.
I've heard that when throwing in a catch handler like this, then you're thrown exception is handled by a hidden try/catch and this passes to the next catch handler. But this is effectively meaning my exception gets black-holed. Which I've heard is a thing and I've read other questions on this website about that.
The actual example isn't this trivial. But the effective code is still the same, a try/catch wrapped around all the code in question. But any exceptions never land in the right place. Then lambda fails with the exception in question. But it's not handled by the code that I wrote, so the response is wrong. I need to handle the code and output a different reply based on what exception is being thrown. So I change it around, fix up the data, add other related information and return that. But since I'm never landing in the right place. I can never do that.
How could I alter this function, so that it correctly lands in the exception handler I've given? Thanks for your help
let getToken = async () => {
let transport = axios.create({ baseURL, headers: passwordHeaders });
return transport.post('/authorization/login')
.then(response => {
let accessToken = _.get(response, 'data.access_token', null);
if(accessToken !== null){
let base64 = Buffer.from(accessToken, 'base64');
let token = base64.toString('ascii');
return token.split(";").shift().split(":").pop();
}
throw new LoginError(response.statusText, response.status));
})
.catch(error => {
if(_.get(error, 'response.status', 'null') === 403){
throw new UnauthorizedError(error.response.statusText);
}
throw error;
});
}
try{
let token = await getToken();
}catch(error){
console.log("this exception should land here");
}
As mentioned in the problems the main problem is the mix of then style and async/await. If we lean into the async approach we could do the following:
let getToken = async () => {
let transport = axios.create({ baseURL });
try {
const response = await transport.post("/authorization/login");
let accessToken = _.get(response, "data.access_token", null);
if (accessToken !== null) {
let base64 = Buffer.from(accessToken, "base64");
let token = base64.toString("ascii");
return token.split(";").shift().split(":").pop();
}
} catch (e) {
throw e;
}
};
try {
let token = await getToken();
} catch (error) {
console.log("this exception should land here");
}
As getToken is an asynchronous function it returns a Promise, returning within this function equals to using Promise.resolve and throwing equals to Promise.reject. In my case the catch block of getToken does not do anything and getToken gets called from within a try / catch. We might want to delegate error handling to the outer try catch by removing the inner one.

How to handle sync browser emulations in node.js

I'm writing a script that is intended to load some stuff from .txt files and then perform multiple ( in a loop) requests to a website with node.js` browser emulator nightmare.
I have no problem with reading from the txt files and so no, but managing to make it run sync and without exceptions.
function visitPage(url, code) {
new Promise((resolve, reject) => {
Nightmare
.goto(url)
.click('.vote')
.insert('input[name=username]', 'testadmin')
.insert('.test-code-verify', code)
.click('.button.vote.submit')
.wait('.tag.vote.disabled,.validation-error')
.evaluate(() => document.querySelector('.validation -error').innerHTML)
.end()
.then(text => {
return text;
})
});
}
async function myBackEndLogic() {
try {
var br = 0, user, proxy, current, agent;
while(br < loops){
current = Math.floor(Math.random() * (maxLoops-br-1));
/*...getting user and so on..*/
const response = await visitPage('https://example.com/admin/login',"code")
br++;
}
} catch (error) {
console.error('ERROR:');
console.error(error);
}
}
myBackEndLogic();
The error that occurs is:
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'webContents' of undefined
So the questions are a few:
1) How to fix the exception
2) How to make it actually work sync and emulate everytime the address ( as in a previous attempt, which I didn't save, I fixed the exception, but the browser wasn't actually openning and it was basically skipped
3) (Not so important) Is it possible to select a few objects with
.wait('.class1,.class2,.validation-error')
and save each value in different variables or just get the text from the first that occured? ( if no any of these has occurred, then return 0 for example )
I see a few issues with the code above.
In the visitPage function, you are returning a Promise. That's fine, except you don't have to create the wrapping promise! It looks like nightmare returns a promise for you. Today, you're dropping an errors that promise returns by wrapping it. Instead - just use an async function!
async function visitPage(url, code) {
return Nightmare
.goto(url)
.click('.vote')
.insert('input[name=username]', 'testadmin')
.insert('.test-code-verify', code)
.click('.button.vote.submit')
.wait('.tag.vote.disabled,.validation-error')
.evaluate(() => document.querySelector('.validation -error').innerHTML)
.end();
}
You probably don't want to wrap the content of this method in a 'try/catch'. Just let the promises flow :)
async function myBackEndLogic() {
var br = 0, user, proxy, current, agent;
while(br < loops){
current = Math.floor(Math.random() * (maxLoops-br-1));
const response = await visitPage('https://example.com/admin/login',"code")
br++;
}
}
When you run your method - make sure to include a catch! Or a then! Otherwise, your app may exit early.
myBackEndLogic()
.then(() => console.log('donesies!'))
.catch(console.error);
I'm not sure if any of this will help with your specific issue, but hopefully it gets you on the right path :)

How do I achieve a synchronous requirement using asynchronous NodeJS

I am adding user validation an data modification page on a node.js application.
In a synchronous universe, in a single function I would:
Lookup the original record in the database
Lookup the user in LDAP to see if they are the owner or admin
Do the logic and write the record.
In an asynchronous universe that won't work. To solve it I've built a series of hand-off functions:
router.post('/writeRecord', jsonParser, function(req, res) {
post = req.post;
var smdb = new AWS.DynamoDB.DocumentClient();
var params = { ... }
smdb.query(params, function(err,data){
if( err == null ) writeRecordStep2(post,data);
}
});
function writeRecord2( ru, post, data ){
var conn = new LDAP();
conn.search(
'ou=groups,o=amazon.com',
{ ... },
function(err,resp){
if( err == null ){
writeRecordStep3( ru, post, data, ldap1 )
}
}
}
function writeRecord3( ru, post, data ){
var conn = new LDAP();
conn.search(
'ou=groups,o=amazon.com',
{ ... },
function(err,resp){
if( err == null ){
writeRecordStep4( ru, post, data, ldap1, ldap2 )
}
}
}
function writeRecordStep4( ru, post, data, ldap1, ldap2 ){
// Do stuff with collected data
}
Additionally, because the LDAP and Dynamo logic are in their own source documents, these functions are scattered tragically around the code.
This strikes me as inefficient, as well as inelegant. I'm eager to find a more natural asynchronous pattern to achieve the same result.
Any promise library should sort your issue out. My preferred choice is bluebird. In summary they help you in performing blocking operations.
If you haven't heard about bluebird then just use it. It converts all function of a module and return promise which is then-able. Simply put, it promisifies all functions.
Here is the mechanism:
Module1.someFunction() \\do your job and finally pass the return object to next call
.then() \\Use that object which is return from the first call, do your job and return the updated value
.then() \\same goes on
.catch() \\do your job when any error occurs.
Hope you understand. Here is an example:
var readFile = Promise.promisify(require("fs").readFile);
readFile("myfile.js",
"utf8").then(function(contents) {
return eval(contents);
}).then(function(result) {
console.log("The result of evaluating
myfile.js", result);
}).catch(SyntaxError, function(e) {
console.log("File had syntax error", e);
//Catch any other error
}).catch(function(e) {
console.log("Error reading file", e);
});
I could not tell from your pseudo-code exactly which async operations depend upon results from with other ones and knowing that is key to the most efficient way to code a series of asynchronous operations. If two operations do not depend upon one another, they can run in parallel which generally gets to an end result faster. I also can't tell exactly what data needs to be passed on to later parts of the async requests (too much pseudo-code and not enough real code to show us what you're really attempting to do).
So, without that level of detail, I'll show you two ways to approach this. The first runs each operation sequentially. Run the first async operation, when it's done, run the next one and accumulates all the results into an object that is passed along to the next link in the chain. This is general purpose since all async operations have access to all the prior results.
This makes use of promises built into the AWS.DynamboDB interface and makes our own promise for conn.search() (though if I knew more about that interface, it may already have a promise interface).
Here's the sequential version:
// promisify the search method
const util = require('util');
LDAP.prototype.searchAsync = util.promisify(LDAP.prototype.search);
// utility function that does a search and adds the result to the object passed in
// returns a promise that resolves to the object
function ldapSearch(data, key) {
var conn = new LDAP();
return conn.searchAsync('ou=groups,o=amazon.com', { ... }).then(results => {
// put our results onto the passed in object
data[key] = results;
// resolve with the original object (so we can collect data here in a promise chain)
return data;
});
}
router.post('/writeRecord', jsonParser, function(req, res) {
let post = req.post;
let smdb = new AWS.DynamoDB.DocumentClient();
let params = { ... }
// The latest AWS interface gets a promise with the .promise() method
smdb.query(params).promise().then(dbresult => {
return ldapSearch({post, dbresult}, "ldap1");
}).then(result => {
// result.dbresult
// result.ldap1
return ldapSearch(result, "ldap2")
}).then(result => {
// result.dbresult
// result.ldap1
// result.ldap2
// doSomething with all the collected data here
}).catch(err => {
console.log(err);
res.status(500).send("Internal Error");
});
});
And, here's a parallel version that runs all three async operations at once and then waits for all three of the to be done and then has all the results at once:
// if the three async operations you show can be done in parallel
// first promisify things
const util = require('util');
LDAP.prototype.searchAsync = util.promisify(LDAP.prototype.search);
function ldapSearch(params) {
var conn = new LDAP();
return conn.searchAsync('ou=groups,o=amazon.com', { ... });
}
router.post('/writeRecord', jsonParser, function(req, res) {
let post = req.post;
let smdb = new AWS.DynamoDB.DocumentClient();
let params = { ... }
Promise.all([
ldapSearch(...),
ldapSearch(...),
smdb.query(params).promise()
]).then(([ldap1Result, ldap2Result, queryResult]) => {
// process ldap1Result, ldap2Result and queryResult here
}).catch(err => {
console.log(err);
res.status(500).send("Internal Error");
});
});
Keep in mind that due to the pseudo-code nature of the code in your question, this is also pseudo-code where implementation details (exactly what parameters you're searching for, what response you're sending, etc...) have to be filled in. This should be illustrative of promise chaining to serialize operations and the use of Promise.all() for parallelizing operations and promisifying a method that didn't have promises built in.

Creating promises in loop

I have to create promises in loop according to given config file and return response when all are resolved. Here goes the code-
{for(let type in spotlight){
switch (type){
case "outliers":{
let ops= spotlight[type];
for(let i=0;i<ops.length;i++){
(function(op){
let p= new Promise(function(resolve,reject){
let reqUrl= urlCreator(op.uri,op.query);
//console.log("--------------------"+reqUrl);
apiService.get(reqUrl,function(isSuccess,data){
if(!isSuccess){
return reject(data);
}
// console.log(isSuccess);
// console.log(data);
// console.log("trend is ------"+JSON.stringify(op));
// create objects array
// let temp= [];
// let overallScore= data.overall.score;
// for(let day in overallScore){
// temp.push({"key": day,"value": parseFloat(overallScore[day])});
// }
//let outliers= stats.outliers(temp,"key","value");
resolve({"type":type,"name": op.name,"data": outliers});
})
});
promiseArray.push(p);
}(ops[i]))
}
break;
}
case "filters":{
let ops= spotlight[type];
for(let i=0;i<ops.length;i++){
(function(op){
let p= new Promise(function(resolve,reject){
let reqUrl= urlCreator(op.uri,op.query);
apiService.get(reqUrl,function(isSuccess,data){
if(!isSuccess){
return reject(data);
}
// console.log(isSuccess);
// console.log(data);
// console.log("coc is ------"+JSON.stringify(op));
resolve({"type": type,"name": op.name,"data": data});
})
})
promiseArray.push(p);
}(ops[i]))
}
break;
}
}
}
Promise.all(promiseArray).then(values=>{
return res.json(values);
},
reason=>{
return res.json(reason);
}).catch(reason=>{
return res.json(reason);
})}
Problem is that promises never return, neither resolved, nor rejected. According to the config file, it has to hit two URLs, say u1 and u2. I tried to log the output to see which requests are returning. When the server is started and very first req is made, U1 returns and req hangs. on refresh I get response from U2,U2 and request hangs, then on refresh again U1,U1 and this continues. It seems to me that for some reason only one request is returned and other sits in buffer or something and come when next request is made. Both requests are being made to the local server only, I am routing it externally just to make use of cache as url is being used as key for cache.
I tried using dummy urls like facebook.com and google.com, and it works perfectly fine.Using one local url and another like facebook.com also works, but when both urls are of local server, it gets stuck.
Does it has any thing to do with single threaded nature of node or due to using same socket for making both requests.
PS- I am using npm-request to make URL calls.
Perhaps hesitating before making the second request would solve your problem.
I've made some tools that could help with that. See the MacroQTools.js file at
https://github.com/J-Adrian-Zimmer/JavascriptPromisesClarified.git
You're defining the request callback as function(success , data), while request consumes error-first callbacks, defined like function(error , response).
You're calling request like:
apiService.get(reqUrl,function(isSuccess,data){
if(!isSuccess){
return reject(data);
}
// console.log(isSuccess);
// console.log(data);
// console.log("coc is ------"+JSON.stringify(op));
resolve({"type": type,"name": op.name,"data": data});
});
Pretending that, if the first parameter misses, you have to reject it with the second parameter, data. While, really, it would something like:
apiService.get(reqUrl,function(err,data){
if(err){
reject(err);
}
else{
// console.log(isSuccess);
// console.log(data);
// console.log("coc is ------"+JSON.stringify(op));
resolve({"type": type,"name": op.name,"data": data});
}
});
Since request expects error-first callbacks (like almost anything in node that takes a callback).
So, when the requests actually work as expected, your code must be actually rejecting the promises with the actual real value, since when the request works, isSuccess is null and data has the real response value.
This surely is breaking something and is not good, while just fixing it maybe doesn't solve your issue completely: I believe your requests are acting weird because some configuration problem of your api, not just because you're rejecting promises when requests are successful (that would just send the data as the rejection reason).
Also you're handling the rejection of Promise.all() twice, passing a second handler to then and calling catch again. Only one is needed, and the .catch(handler) is probably better.
I made a small working example on how you can use Promise.all to collect async requests. I used imdb as the apiService, but any async http service would work too. I didn't reproduce totally from your code, but I'm sure you can adapt this to make your code work, at least the part of the code that is just consuming http services.
var express = require('express');
var app = express();
var Promise = require('bluebird');
var imdb = require('imdb-api');
app.get('/', controllerHandler );
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
});
var apiService = {}
apiService.get = imdb.getReq;
function controllerHandler(request , response){
//like iterating through spotlight.type and returning an array of promises from it.
//in this case the array is from films and Airbag is obviously the best of them
var promises = [{name : 'The Matrix'} , { name : 'Avatar'} , {name : 'Airbag'}].map( createPromise );
//use either .catch(errorHandler) or then( successHandler , errorHandler ). The former is the better:
Promise.all(promises).then( successHandler ).catch( errorHandler );
function successHandler(result){
return response.json(result);
}
function errorHandler(reason){
console.log('There was an error calling to the service:');
console.log(reason);
return response.send('there was an error');
}
}
function createPromise(film){
return new Promise( function(resolve , reject){
apiService.get(film , function(err , data){
if(err)
reject( new Error(err));
else
resolve( {title : data.title , year : data.year} );
});
});
};

Resources