Tests with Sinon + Chai fail after refactor to implement promises - node.js

I got a lot of callbacks in this application that ended up as a good example of a 'callback-hell'. Methods with 200 or 300 lines and 10, 15 nested callbacks each.
Then, as a good practice, I detached most of these callbacks, named then and created more organized 'then-chains' with Bluebird promises.
I don't know exactly why, since all tests and methods I have implemented are very similar to each other, two tests cases of the same method failed.
It was using Sinon to check whether or not the next function and a DAO method (that makes a Mongo query) were being called.
I then realized that what changed in this method was that I transfered some logic that was running in the beginning of that method to inside a promise.
The call to 'next' was not getting evaluated to true because the test didn't wait for the promise to complete and returned wrongly.
This is the part of the test that showed a bad behavior:
var nextSpy = sinon.spy();
var methodSpy = sinon.spy(my_controller.options.dao, "findAsync");
my_controller.sendMail(req, res, nextSpy);
expect(methodSpy.called).to.be.true;
expect(sendMailSpy.called).to.be.false;
I have changed the controller method, but basically, i had the logic inside of a promise. puting it out solves the problem.

Related

Adding a value from Mongoose DB into a variable in Node.js

I am still quite new to Node.js and can't seem to find anything to help me around this.
I am having an issue of getting the query from my last record and adding it to my variable.
If I do it like below: -
let lastRecord = Application.find().sort({$natural:-1}).limit(1).then((result) => { result });
Then I get the value of the variable showing in console.log as : -
Promise { <pending> }
What would I need to do to output this correctly to my full data?
Here is it fixed:
Application.findOne().sort({$natural:-1}).exec().then((lastRecord) => {
console.log(lastRecord); // "lastRecord" is the result. You must use it here.
}, (err) => {
console.log(err); // This only runs if there was an error. "err" contains the data about the error.
});
Several things:
You are only getting one record, not many records, so you just use findOne instead of find. As a result you also don't need limit(1) anymore.
You need to call .exec() to actually run the query.
The result is returned to you inside the callback function, it must be used here.
exec() returns a Promise. A promise in JavaScript is basically just a container that holds a task that will be completed at some point in the future. It has the method then, which allows you to bind functions for it to call when it is complete.
Any time you go out to another server to get some data using JavaScript, the code does not stop and wait for the data. It actually continues executing onward without waiting. This is called "asynchronisity". Then it comes back to run the functions given by then when the data comes back.
Asynchronous is simply a word used to describe a function that will BEGIN executing when you call it, but the code will continue running onward without waiting for it to complete. This is why we need to provide some kind of function for it to come back and execute later when the data is back. This is called a "callback function".
This is a lot to explain from here, but please go do some research on JavaScript Promises and asynchronisity and this will make a lot more sense.
Edit:
If this is inside a function you can do this:
async function someFunc() {
let lastRecord = await Application.findOne().sort({$natural:-1}).exec();
}
Note the word async before the function. This must me there in order for await to work. However this method is a bit tricky to understand if you don't understand promises already. I'd recommend you start with my first suggestion and work your way up to the async/await syntax once you fully understand promises.
Instead of using .then(), you'll want to await the record. For example:
let lastRecord = await Application.find().sort({$natural:-1}).limit(1);
You can learn more about awaiting promises in the MDN entry for await, but the basics are that to use a response from a promise, you either use await or you put your logic into the .then statement.

What does "dispatch()" mean/do, and why is it used when we have .then() and .catch()

I am new to ES6 and advanced javascript. I have seen examples of code using the axios http client like this:
axios.xxx(...).then((res) => dispatch(success(res)) , (err)=> dispatch(error(err)))
whereas I am doing:
axios.xxx(...).then(function(res){...}).catch(function(err){...});
I tried to look up dispatch on MDN but only found DispatchEvent... which is not the same? I ask because although my code works, I am finding http error codes like 403 etc from my api are handled as errors by axios, while i would prefer to handle them myself in the app. (Update: when I added the dispatch tag to this question, I saw a brief summary of the meaning but I am still confused).
What is the reason or advantage for using dispatch? Is "dispatch()" part of axios, or ES6, or nodejs? thx.
When I see dispatch I immediately think of redux-thunk (a popular middleware for Redux). It is a good example of why passing dispatch is useful. Basically dispatch is used as a callback which gets invoked once some async action is complete. In redux-thunk dispatch is simply a function which dispatches an action to the Redux store after, let's say, you fetch data from an API (which is asynchronous). You can pass any function you like to .then() or .catch() of some Promise and it will be invoked upon success or failure.

Can I make Chai's expect.to.not.throw synchronous?

I have a snippet of code that I'm testing in Chai and Pact. It looks something like this:
var myVerify = () => {
provider.verify().then(() => {
console.log('B verified')
done()
})
}
expect(myVerify).to.not.throw()
This works but it's a lot of extra work to go through to make a wrapper function to ensure that I wait on Pact's verify complete's before continuing on with the test. Pact has some internal state that will clear when it's done. If I just call this:
expect(provider.verify()).to.not.throw()
then it will conflict with other tests.
This code seems to work fine for me but it's very messy. Is there a simpler way to accomplish this?
I wouldn't recommend this method since if an error did in fact occur, it would never be caught anyways because promises don't "throw errors", they simply reject the promise, which you can catch using .catch or being the second parameter of the .then.
There are 2 ways of doing what you want:
Just with Mocha:
return provider.verify().then(() => {
console.log('B verified');
done();
}, () => throw new Error("B verification failed"));
In this simple example, we're not using chai to verify anything since you're not actually verifying data output of the verify, you're just checking to see if the promise was a success, if not, throw an error which will fail your tests. Mocha, by default, understands promises as long as they're returned as part of the test.
However, this method means that the wrapping it function needs to inject the done parameter, which I'm not a fan of. What I am a fan of is using:
Chai with Chai as Promised:
You need to set up Chai as Promised using
chai.use(require("chai-as-promised))
then in your tests, simply do:
return expect(provider.verify()).to.eventually.be.fulfilled;
This test will wait for the promise to return, and chai will validate that it is, in fact, fulfilled and not rejected. I find this syntax to be much simpler to use and makes writing tests simpler. You can also have multiple expects with the same promise, using Promises.all:
var verify = provider.verify();
return Promises.all(
expect(verify).to.eventually.be.fulfilled,
expect(verify).to.eventually.be.true,
);

using promises in node.js to create and compare two arrays

I needed to compare two arrays the first one a couple of filenames from a database, the second one a list of files I already downloaded to my client. The Idea was to load whatever files are missing on the client.
As the reading via fswas two slow, I tried using Promises to wait for one function to finish before the next starts. But somehow I got lost...
My code so far:
let filesIneed = [];
let filesIhave = [];
let filesToFetch = [];
getLocalFiles().then(getFilesIneed).then(getfilesToRetreive);
function getLocalFiles() {
fs.readdir(localPath, (err, files) => {
files.forEach(file => {
filesIhave.push(file)
});
})
return Promise.all(filesIhave);
}
function getFilesIneed () {
for (let x of docs) {//this is my JSON
filesIneed.push(y.NameOfFileIShouldHave);
}
}
return Promise.all(filesIneed);
}
function getfilesToRetreive() {
filesToFetch = _.difference(filesIneed, filesIhave);
return Promise.all(filesToFetch);
}
console.log(filesToFetch);
I do get the first and second array ("filesIneed" and "filesIhave"), but difference is always empty. So maybe I just mangled up the Promises, as this concept is completely new to me and I'm aware I only understood half of it.
This is completely wrong. You cannot run Promise.all on an array of filenames. You can only run it on an array of promises.
There is also no need to push every element of an array one at a time to an empty array just to return that array when you already have that array in the first place.
You cannot use promises to compare two arrays. You can use lodash to compare two arrays in a then handler of a promise, that resolves to an array.
If you want to get a promise of file names from the fs.readdir then use one of the following modules:
https://www.npmjs.com/package/mz
http://bluebirdjs.com/docs/api/promise.promisifyall.html
https://www.npmjs.com/package/fs-promise
https://www.npmjs.com/package/fs-promised
Also don't use global variables for everything because you will have problems with any concurrency.
Also, read about promises. Without understanding how promises work you will not be able to guess a correct way of using them. Even looking at some working code examples can help a lot and there are a lot of questions and answers on stack Overflow about promises:
promise call separate from promise-resolution
Q Promise delay
Return Promise result instead of Promise
Exporting module from promise result
What is wrong with promise resolving?
Return value in function from a promise block
How can i return status inside the promise?
Should I refrain from handling Promise rejection asynchronously?
Is the deferred/promise concept in JavaScript a new one or is it a traditional part of functional programming?
How can I chain these functions together with promises?
Promise.all in JavaScript: How to get resolve value for all promises?
Why Promise.all is undefined
function will return null from javascript post/get
Use cancel() inside a then-chain created by promisifyAll
Why is it possible to pass in a non-function parameter to Promise.then() without causing an error?
Implement promises pattern
Promises and performance
Trouble scraping two URLs with promises
http.request not returning data even after specifying return on the 'end' event
async.each not iterating when using promises
jQuery jqXHR - cancel chained calls, trigger error chain
Correct way of handling promisses and server response
Return a value from a function call before completing all operations within the function itself?
Resolving a setTimeout inside API endpoint
Async wait for a function
JavaScript function that returns AJAX call data
try/catch blocks with async/await
jQuery Deferred not calling the resolve/done callbacks in order
Returning data from ajax results in strange object
javascript - Why is there a spec for sync and async modules?
Return data after ajax call success

Code coverage for node.js project using Q promises

I'm currently working on a node.js project. We use Q library for promises (https://github.com/kriskowal/q).
We are using mocha for tests and code-coverage provided with grunt tasks (https://github.com/pghalliday/grunt-mocha-test) which uses blanket.js for coverage and travis-cov reporter for asserting the coverage threshold.
Unfortunately the solution does not provide branches coverage for promises.
I have tried Intern (http://theintern.io/), however basic example I wrote does not show correct branch coverage too.
Can you recommend a solution that would provide correct coverage for Q promises and works with grunt and node seamlessly?
Well, checking promises for coverage should not be too hard because JavaScript has absolutely sick aspect oriented programming abilities. While automated tools are offtopic here, let's go through what branches a promise has:
First, the resolver function for the promise (either a promise constructor or a deferred) have their own branches.
Each .then clause has 3 branches (one for success, one for failure, one for progress), if you want loop coverage too, you'd want each progress event when progress is attached to fire zero times, once and multiple times - although let's avoid progress for this question and in general since it's being deprecated and replaced in Q.
The .done, .catch etc... are all private cases of .then. Aggregation methods like .all are private cases of the promise constructor since they create a new aggregate promise and their .then needs to be checked just as well.
So, let's see how we can do this, first - note that Q uses Promise.prototype.then internally for aggregation methods, so we can override it:
Promise.prototype._then = Promise.prototype.then;
var branches = 0;
Promise.prototype.then = function(fulfilled,rejected,progressed){
branches += (fulfilled.call) +(rejected.call) + (progressed.call);
var nFulfilled = function(){ branches--;return fulfilled.apply(this,arguments); };
var nRejected = function(){ branches--; return rejected.apply(this,arguments); };
//progression may happen more than once
var nProgressed = function(){
if(!nProgress.happened) {
branches--;
nProgress.happened = true;
}
return progressed.apply(this,arguments);
};
nProgressed.happened = false;
return this._then((fulfilled.call) ? nFulfilled : undefined,
(rejected.call) ? nRejected : undefined,
(progressed.call) ? nProgressed : undefined);
};
What we're doing here, is hooking on every .then and keeping track of handlers attached and handlers being called.
The branches variable will contain the number of code paths created with promises, and not actually executed. You can use more sophisticated logic here (for example - count rather than subtract) and I'd like to see someone pick up the glove and make a git repo from this :)

Resources