Using promises to wait for function to finish - javascript

I've read some tutorials online for the Promises method but I'm still a bit confused.
I have a Node app.js which performs several functions including connecting to a db.
db.connect(function(err) {
setupServer();
if(err) {
logger.raiseAlarmFatal(logger.alarmId.INIT,null,'An error occurred while connecting to db.', err);
return;
}
Now I have written a mocha unit test suite, which encapsulates this app and performs several request calls to it. In some cases what occurs is that the the test initializes without confirmation that the db has successfully connected i.e: setupServer() has been performed.
How would I implement the promises method to this bit of asynchronous code, and if not promises, what should I use ? I have already tried event emitter but this still does not satisfy all the requirements and causes failures during cleanup.

If you're using mocha, you should use asynchronous code approach. This way you can instruct mocha to wait for you to call done function before it goes on with the rest.
This would get you started:
describe('my test', function() {
before(function(done) {
db.connect(function(err) {
setupServer(done);
});
})
it('should do some testing', function() {
// This test is run AFTER 'before' function has finished
// i.e. after setupServer has called done function
});
});
assuming that your setupServer calls the done function when it's done:
function setupServer(done) {
// do what I need to do
done();
}

You will need to use Promise inside the body of function that has async work. For your case, I think that is setupServer() which you said contains ajax requests.
conts setupServer = () => {
return new Promise((resolve, reject) => {
//async work
//get requests and post requests
if (true)
resolve(result); //call this when you are sure all work including async has been successfully completed.
else
reject(error); //call this when there has been an error
});
}
setupServer().then(result => {
//...
//this will run when promise is resolved
}, error => {
//...
//this will run when promise is rejected
});
For further reading:
Promise - MDN

Related

Jest testing with Node - Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout

I'm starting to test my code with Jest, and I can't make a seemingly simple test to pass. I am simply trying to check if what I receive from a Maogoose database request is an object.
The function fetchPosts() is working because I hooked it up with a React frontend and it is displaying the data correctly.
This is my function fetchPosts():
module.exports = {
fetchPosts() {
return new Promise((resolve, reject) => {
Posts.find({}).then(posts => {
if (posts) {
resolve(posts)
} else {
reject()
}
})
})
}
}
And my test:
it('should get a list of posts', function() {
return posts.fetchPosts().then(result => {
expect(typeof result).toBe('object')
})
})
This makes the test fail, and Jest says
'Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.'
QUESTION: How can I make this test pass?
You can expect asynchronous results using resolves, as shown in the Jest documentation.
In your case:
it('should get a list of posts', function() {
const result = posts.fetchPosts();
expect(result).resolves.toEqual(expect.any(Object));
})
…although I have a suspicion your list of posts is actually an array, so you probably want this:
it('should get a list of posts', function() {
const result = posts.fetchPosts();
expect(result).resolves.toEqual(expect.any(Array));
})
Another tip: You don't need to wrap the body of your fetchPost in an additional promise, you can simply return the promise you get from Posts.find and add a then to it, like this:
module.exports = {
fetchPosts() {
return Posts.find({}).then(posts => {
if (posts) {
return posts;
}
throw new Error('no posts'); // this will cause a promise rejection
})
}
}
It's also highly possible that you're not getting a response back from the DB at all from your test suite. Test suite's can call different environmental variables / configs that lead to different calls. This error can also be seen if no response is returned, as in - if someone blocks your IP from connecting, on and on.
Also if you are simply looking to increase the timeout, then you can do that by setting
jest.setTimeout(10000);
You can use this statement in beforeEach if you want to change the timeout for all your tests in that describe block or in the test/it/spec block if you want it for a single test.
For me none of the above worked so I tried older version of jest and it worked
npm i -D jest#25.2.7.
if you are using it with typescript make sure to degrade ts-jest as well
npm i -D jest#25.2.7 ts-jest#25.3.1

Unit testing NodeJS Promise inside a function

Im trying to unit test a function that calls a promise...
Using Mocha, Sinon. I have a functional block like this:
myfile.js:
let OuterDependecy = require('mydep');
function TestFunction(callback) {
OuterDependency.PromiseFunction().then(response => {
//some logic here
}).catch(err => {callback(err)});
inside my test i have used proxyquire to mock the outerdependecy
testfile.js
let proxyquire = require('proxyquire');
let OuterDepStub = {};
let testingFunc = proxyquire('myfile.js', {'mydep': OuterDepStub});
... then inside my testing block
let stubCallback = function() {
console.log('Stub dubadub dub'); //note...i can use sinon.spy here instead
};
beforeEach(()=>{
OuterDependency.PromiseFunction = function(arg) {
return new Promise((resolve, reject)=>{
reject('BAD');
});
};
spy = sinon.spy(stubCallback);
});
my actual test now calls the main "testfunction"
it('Catches Errors, and calls back using error', done => {
TestFunction(stubCallback);
expect(spy).to.have.been.called;
done();
});
I see the stub being called (the console log, hence why i didnt want to use sinon.spy) but the spy is saying its not called. and unit test fails.
I believe this is probably due to a race condition of sorts where the promise is resolving after my test is run... is there anyway to delay the test until my promise is resolve.
i know in in angularjs promise testing, there was a way to "tick" the promise so it resolves when you want to. possible in nodejs?
is there anyway to delay the test until my promise is resolve.
As far as I understand your issue, yes, you should only call done() after the promise is settled. In order to do that,you need two things:
1- Enforce TestFunction to return a Promise, so you can wait until it resolves:
function TestFunction(callback) {
return OuterDependency.PromiseFunction().then(response => {
//some logic here
}).catch(err => { callback(err) });
}
2- Wait to that promise to settle, then call done.
it('Catches Errors, and calls back using error', done => {
TestFunction(stubCallback).then(() => {
expect(spy).to.have.been.called;
done();
})
});
now, our then block won't run until the catch block within TestFunction, so if the test works as expected (i.e. the catch block fires and the callback gets fired), the expectation and the done calls will always fire after the callback gets called.
I see the stub being called (the console log, hence why i didnt want to use sinon.spy) but the spy is saying its not called. and unit test fails.
That's because your expectation runs right after the TestFunction calls, without waiting for it to settle. However, it will get called lately, thus the console.log appears in the next spec.

Test callback invocation at the end of promise chain

I am dealing with a code mixing node-style callbacks and Bluebird promises, and I need to write some unit tests for it.
In particular, cache.js exposes the init() function, which works with promises. It is then called by the doSomething() function in another file (e.g. index.js) which in turn accepts a callback that has to be invoked at the end of init().
Pseudocode is as follows:
// [ cache.js ]
function init() {
return performInitialisation()
.then((result) => return result);
}
// [ index.js ]
var cache = require('./cache');
function doSomething(callback) {
console.log('Enter');
cache.init()
.then(() => {
console.log('Invoking callback');
callback(null);
})
.catch((err) => {
console.log('Invoking callback with error');
callback(err);
});
console.log('Exit');
}
A possible unit test could be (showing only relevant code):
// [ index.test.js ]
...
var mockCache = sinon.mock(cache);
...
it('calls the callback on success', function(done) {
mockCache.expects('init')
.resolves({});
var callback = sinon.spy();
doSomething(callback);
expect(callback).to.have.been.calledOnce;
done();
});
This test passes, however changing the expectation to not.have.been.calledOnce also passes, which is wrong.
Also, console logs are out of sequence:
Enter
Exit
Invoking callback
I have looked at several possibilities, none of which worked:
Using chai-as-promised, e.g. expect(callback).to.eventually.have.been.calledOnce;
Refactoring doSomething() to be simply:
function doSomething(callback) {
cache.init()
.asCallback(callback);
}
Can anyone help me understand what I am doing wrong and how I can fix it please?
console logs are out of sequence
The logs are in the correct order because your Promise will be async meaning, at the very least, the internal console logs calls in then & catch will run on the next tick.
As to why the test is failing is the result of a couple of issues, first one is you don't appear to have sinon-chai configured correctly, or at best your calledOnce assertion isn't kicking in. Just to confirm, the top of your test file should something like:
const chai = require("chai");
const sinonChai = require("sinon-chai");
chai.use(sinonChai);
If you have that and it's still not working correctly then might be worth opening an issue on the sinon-chai lib, however, a simple workaround is to switch to sinon assertions e.g.
sinon.assert.calledOnce(callback)
Secondly, when you do eventually fix this, you'll probably find that the test will now fail...everytime. Reason being you've got the same problem in your test that you have with your logging - your asserting before the internal promise has had a chance to resolve. Simplest way of fixing this is actually using your done handler from Mocha as your assertion
mockCache.expects('init').resolves({});
doSomething(() => done());
In other words, if done get's called then you know the callback has been called :)
Following James' comment I revisited my tests like this:
it('calls the callback on success', function(done) {
mockCache.expects('init')
.resolves({});
doSomething(done);
});
it('calls the callback on error', function(done) {
mockCache.expects('init')
.rejects('Error');
doSomething((err) => {
if (err === 'Error') {
done();
} else {
done(err);
}
});
});

nodejs: How to wait for several asynchronous tasks to finish

I have a file where I'm writing things:
var stream = fs.createWriteStream("my_file.txt");
stream.once('open', function(fd) {
names.forEach(function(name){
doSomething(name);
});
stream.end();
});
This is working ok and I'm able to write to the file.
The problem is that the doSomething() function has some parts that are asynchronous. An example can be given with the dnsLookup function. Somewhere in my doSomething() I have:
dns.lookup(domain, (err, addresses, family) => {
if(err){
stream.write("Error:", err);
}else{
stream.write(addresses);
}
});
Now, my problem is, since the DNS check is asynchronous, the code keeps executing closing the stream. When the DNS response finally comes it cannot write to anywhere.
I already tried to use the async module but it didn't work. Probably I did something wrong.
Any idea?
Now that NodeJS is mostly up to speed with ES2015 features (and I notice you're using at least one arrow function), you can use the native promises in JavaScript (previously you could use a library):
var stream = fs.createWriteStream("my_file.txt");
stream.once('open', function(fd) {
Promise.all(names.map(name => doSomething(name)))
.then(() => {
// success handling
stream.end();
})
.catch(() => {
// error handling
stream.end();
});
});
(The line Promise.all(names.map(name => doSomething(name))) can be simply Promise.all(names.map(doSomething)) if you know doSomething ignores extra arguments and only uses the first.)
Promise.all (spec | MDN) accepts an iterable and returns a promise that is settled when all of the promises in the iterable are settled (non-promise values are treated as resolved promises using the value as the resolution).
Where doSomething becomes:
function doSomething(name) {
return new Promise((resolve, reject) => {
dns.lookup(domain, (err, addresses, family) => {
if(!err){ // <== You meant `if (err)` here, right?
stream.write("Error:", err);
reject(/*...reason...*/);
}else{
stream.write(addresses);
resolve(/*...possibly include addresses*/);
});
});
});
There are various libs that will "promise-ify" Node-style callbacks for you so using promises is less clunky than the mix above; in that case, you could use the promise from a promise-ified dns.lookup directly rather than creating your own extra one.

should.js not causing mocha test to fail

I'm very new to unit tests, mocha, and should.js, and I'm trying to write a test for an asynchronous method that returns a promise. Here is my test code:
var should = require("should"),
tideRetriever = require("../tide-retriever"),
moment = require("moment"),
timeFormat = "YYYY-MM-DD-HH:mm:ss",
from = moment("2013-03-06T00:00:00", timeFormat),
to = moment("2013-03-12T23:59:00", timeFormat),
expectedCount = 300;
describe("tide retriever", function() {
it("should retrieve and parse tide CSV data", function() {
tideRetriever.get(from, to).then(
function(entries) { // resolve
entries.should.be.instanceof(Array).and.have.lengthOf(expectedCount);
},
function(err) { // reject
should.fail("Promise rejected", err);
}
);
});
});
When I manually test the tideRetriever.get method, it consistently resolves an array of 27 elements (as expected), but the test will not fail regardless of the value of expectedCount. Here is my simple manual test:
tideRetriever.get(from, to).then(
function(entries) {
console.log(entries, entries.length);
},
function(err) {
console.log("Promise rejected", err);
}
);
I can also post the source for the module being tested if it's necessary.
Am I misunderstanding something about Mocha or should.js? Any help would be greatly appreciated.
UPDATE
At some point Mocha started to support returning Promise from test instead of adding done() callbacks. Original answer still works, but test looks much cleaner with this approach:
it("should retrieve and parse tide CSV data", function() {
return tideRetriever.get(from, to).then(
function(entries) {
entries.should.be.instanceof(Array).and.have.lengthOf(expectedCount);
}
);
});
Check out this gist for complete example.
ORIGINAL
CAUTION. Accepted answer works only with normal asynchronous code, not with Promises (which author uses).
Difference is that exceptions thrown from Promise callbacks can't be caught by application (in our case Mocha) and therefore test will fail by timeout and not by an actual assertion. The assertion can be logged or not depending on Promise implementation. See more information about this at when documentation.
To properly handle this with Promises you should pass err object to the done() callback instead of throwing it. You can do it by using Promise.catch() method (not in onRejection() callback of Promise.then(), because it doesn't catch exceptions from onFulfilment() callback of the same method). See example below:
describe("tide retriever", function() {
it("should retrieve and parse tide CSV data", function(done) {
tideRetriever.get(from, to).then(
function(entries) { // resolve
entries.should.be.instanceof(Array).and.have.lengthOf(expectedCount);
done(); // test passes
},
function(err) { // reject
done(err); // Promise rejected
}
).catch(function (err) {
done(err); // should throwed assertion
});
});
});
PS done() callback is used in three places to cover all possible cases. However onRejection() callback can be completely removed if you don't need any special logic inside it. Promise.catch() will handle rejections also in this case.
When testing asynchronous code, you need to tell Mocha when the test is complete (regardless of whether it passed or failed). This is done by specifying an argument to the test function, which Mocha populates with a done function. So your code might look like this:
describe("tide retriever", function() {
it("should retrieve and parse tide CSV data", function(done) {
tideRetriever.get(from, to).then(
function(entries) { // resolve
entries.should.be.instanceof(Array).and.have.lengthOf(expectedCount);
done();
},
function(err) { // reject
should.fail("Promise rejected", err);
done();
}
);
});
});
Note that the way Mocha knows this is an async test and it needs to wait until done() is called is just by specifying that argument.
Also, if your promise has a "completed" handler, which fires both on success and failure, you can alternatively call done() in that, thus saving a call.
More info at:
http://mochajs.github.io/mocha/#asynchronous-code

Categories