test promise reject case using mocha - javascript

I want to make test case for promise reject case.
Here is my code
1) File: TemplateFinder.js
**I want to make test for this method on promise reject using *mocha* how can i achieve this?**
var findTemplate = function (template) {
return templateFinder.find(templateName)
.then( (result) => {
return result.id;
});
};
2) query.js
var find = function (template) {
return knex.where({
internal_name: template,
})
.select('external_id')
.from('mailing_external_template')
.then(function (data) {
if (0 === data.length) {
return Promise.reject(RangeError('no result for id'));
} else {
return data[0].external_id;
}
});
};
3) app.js
Main file to run
function findTemplate()
{
templateFinder.findGoodTemplate('good-mail-template');
}
initiateProcess()
.then(function (fromResolve) {
return findTemplate();
})
.catch(function (e) {
});
My try:
it('should throw error if template not found', function () {
var findTemplateIdStub = sinon.stub(templateFinder, 'find');
var error = 'my expected error message returned by my function';
findTemplateIdStub.returns(Promise.reject(error));
return templateFinder.findTemplate(templateName)
.catch(function(err) {
assert.equal(err, error);
});
});
the problem with my try is findTemplate has no catch so how can i am asserting variables from my test case, not original. (Catch is not in code but still my test pass.) Please help me to make test case for findTemplate reject case.

Is findTemplateIdStubsupposed to return a promise?
If that's the case, you can include sinon-as-promised and the stub should be:
findTemplateIdStub.rejects('some error') for the bad case
findTemplateIdStub.resolves(1234) for the good case

Related

How to call an API twice if there is an error occurred?

I have an internal API that I would like to post data. Depends on some cases, I am seeing errors. So what I would like to do is to call it again if there is an error occurred.
What I did was to create a counter to pass it to the function and call the function recursively as below. This gives me the error as below:
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
Here is how I call the function:
....
private RETRY_API = 1;
....
try {
await this.callAPI(request, this.RETRY_API);
} catch (error) {
console.log('error', error);
}
This program never comes to the catch block above.
And here is my actual function that I call the API:
private async callAPI(request, retry) {
return new Promise((resolve, reject) => {
someService.postApiRequest('api/url', request, async(err: any, httpCode: number, data) => {
if (this.RETRY_API == 2) {
return reject(err);
} else if (err) {
this.callAPI(request, retry);
this.RETRY_API++;
} else if ( httpCode !== 200 ) {
this.RETRY_API = 2;
// some stuff
} else {
this.RETRY_API = 2;
// some stuff
return resolve(data);
}
});
})
}
Not sure what I am missing. If there is a better way to call the API twice if an error occurred, that would be great if you let me know.
Let's organize a little differently. First, a promise-wrapper for the api...
private async callAPI(request) {
return new Promise((resolve, reject) => {
someService.postApiRequest('api/url', request,(err: any, httpCode: number, data) => {
err ? reject(err) : resolve(data);
});
});
}
A utility function to use setTimeout with a promise...
async function delay(t) {
return new Promise(resolve => setTimeout(resolve, t));
}
Now, a function that calls and retries with delay...
private async callAPIWithRetry(request, retryCount=2, retryDelay=2000) {
try {
return await callAPI(request);
} catch (error) {
if (retryCount <= 0) throw err;
await delay(retryDelay);
return callAPIWithRetry(request, retryCount-1, retryDelay);
}
}
If you can't force a failure on the api to test the error path some other way, you can at least try this...
private async callAPIWithRetry(request, retryCount=2, retryDelay=2000) {
try {
// I hate to do this, but the only way I can test the error path is to change the code here to throw an error
// return await callAPI(request);
await delay(500);
throw("mock error");
} catch (error) {
if (retryCount <= 0) throw err;
await delay(retryDelay);
return callAPIWithRetry(request, retryCount-1, retryDelay);
}
}
It looks like you need to add return await to the beginning of the line this.callAPI(request, retry); in callAPI function.
Similarly there are some condition blocks that doesn't resolve or reject the promise. While it might work okay, it's considered bad practice. You want to either resolve or reject a promise.
I've accomplished calling an API a second time when I received an error by using axios' interceptors functions.
Here is a code snippet you can review:
axios.interceptors.response.use(
// function called on a successful response 2xx
function (response) {
return response;
},
// function called on an error response ( not 2xx )
async function (error) {
const request = error.config as AxiosRequestConfig;
// request is original API call
// change something about the call and try again
// request.headers['Authorization'] = `Bearer DIFFERENT_TOKEN`;
// return axios(request)
// or Call a different API
// const new_data = await axios.get(...).then(...)
// return new_data
// all else fails return the original error
return Promise.reject(error)
}
);
Try replacing
if (this.RETRY_API == 2)
with
if (this.RETRY_API > 1)

Jest mocks and error handling - Jest test skips the "catch" of my function

I'm creating a jest test to test if metrics were logged for the error handling of the superFetch function. My approach is creating a mock function for retryFetch and returning a Promise reject event. I expect that to go to the superFetch catch but it keeps ending up in superFetch then. What can I do to handle my errors in superFetch catch?
These are the functions:
// file: fetches.js
export function retryFetch(url) {
return new Promise((resolve, reject) => {
fetch(url).then(response => {
if (response.ok) {
resolve(response);
return;
}
throw new Error();
}).catch(error => {
createSomething(error).then(createSomething => {
reject(createSomething);
});
return;
});
});
});
export function superFetch(url, name, page) {
return retryFetch(url)
.then(response => {
return response;
}).catch(error => {
Metrics.logErrorMetric(name, page);
throw error;
});
}
My jest test:
import * as fetch from '../../src/utils/fetches';
describe('Fetch fails', () => {
beforeEach(() => {
fetch.retryFetch = jest.fn(() => Promise.reject(new Error('Error')));
});
it('error metric is logged', () => {
return fetch.superFetch('url', 'metric', 'page').then(data => {
expect(data).toEqual(null);
// received data is {"ok": true};
// why is it even going here? im expecting it to go skip this and go to catch
}).catch(error => {
// this is completely skipped. but I'm expecting this to catch an error
// received error is null, metric was not called
expect(Metrics.logErrorMetric).toHaveBeenCalled();
expect(error).toEqual('Error');
});
});
});
The problem is that you overwrite the function in the exported module but superFetch use the original one inside of the module, so the overwrite will have no effect.
You could mock fetch directly like this:
global.fetch = jest.mock(()=> Promise.reject())

Returning one object that's built with nested promises

I'm struggling to wrap my head around a nested promise layout where one one object is returned at the end of it. My current code is as follows:
router
router.get(`/${config.version}/event/:id?`, function (req, res, next) {
var event = new Event(req, res, next);
event.getInfo(req.params.id).then((info) => {
res.send(info);
});
});
function
getInfo(id) {
db.main('events').where('id', id).select()
.then((result) => {
if(result.length > 0) {
var event = result[0];
//regular functions
event.status = this.getStatus(id);
event.content = this.getContent(id);
event.price = this.getPrice(id);
//promise functions
var users = this.getUsers(id);
var hosts = this.getHosts(id);
Promise.all([users, hosts]).then(values => {
event.users = values[0];
event.hosts = values[1];
//return whole event object to router
return event;
})
.catch((err) => {
return {
result: 'error',
error: err
};
});
} else {
return {
result: 'error',
error: "Event does not exist"
};
}
}).catch((e) => {
return {
result: 'error',
error: "Could not retrieve event info"
};
});
}
As you can see, the router initiates a call to get info about an event. The function then does a database call and gets some event data. Thereafter I need to get the users and hosts of the event from a different table, append that info to the event object as well and then return the whole object to the router to be sent to the client.
When I do this I get an error because I'm not returning a promise from the getInfo function, but I'm not sure how or which promise I'm supposed to return.
I'd appreciate some help with this. Thanks
using .then means that you are returning a promise.
function getInfo(id) {
return new Promise(function(resolve, reject) {
resolve('yay!');
})
}
getInfo().then(function(result) { //result = yay! });
to make your code work, simply replace all the returns with resolves, the errors with rejects, and wrap the whole thing with a return new Promise as i did.
getInfo(id) {
return new Promise(function(resolve, reject) {
db.main('events').where('id', id).select()
.then((result) => {
if (result.length > 0) {
var event = result[0];
//regular functions
event.status = this.getStatus(id);
event.content = this.getContent(id);
event.price = this.getPrice(id);
//promise functions
var users = this.getUsers(id);
var hosts = this.getHosts(id);
Promise.all([users, hosts]).then(values => {
event.users = values[0];
event.hosts = values[1];
//return whole event object to router
resolve(event);
})
.catch((err) => {
reject({
result: 'error',
error: err
});
});
} else {
reject({
result: 'error',
error: "Event does not exist"
});
}
}).catch((e) => {
reject({
result: 'error',
error: "Could not retrieve event info"
});
});
});
}
Just wrap your async code in Promise like this:
getInfo(id) {
return new Promise(function(resolve, reject) {
db.main('events').where('id', id).select()
.then((result) => {
//...
resolve(/* result */)
// OR
reject(/* Error */)
})
}
Note: Use resolve and reject instead return
It's a combination of several things, but the main one is that you are never returning anything from getInfo, so your router handler is calling .then on undefined.
Do not call .catch (without throwing inside it) on Promises you intend to return for a caller to consume. This makes it not possible to use .catch, because you recovered the Promise chain into a resolved one.
Whatever you return inside a .then will be merged into the promise chain, so it's not actually a "Promise that resolves with a Promise". Your whole code could be replaced with:
getInfo (id) {
return db.main('events').where('id', id).select()
.then(result => {
if (result.length == 0) {
// you can also just throw your error object thing,
// but standard Error are generally the convention
throw new Error('Event does not exist')
}
const [event] = result
event.status = this.getStatus(id)
event.content = this.getContent(id)
event.price = this.getPrice(id)
return Promise.all([this.getUsers(id), this.getHosts(id)])
.then(([users, hosts]) => {
event.users = users
event.hosts = hosts
// this is the only value that
// this.getInfo(id).then(value => {/* ... */}) will see
return event
}
})
}

Promise reject not working inside of callback

I'm writing a module that uses the Google API, but am wrapping everything that is callback based in a promise. This is the code of the problem area
file1.js
var File2 = require('file2')
var api = new File2()
api.auth().then(auth => {
api.search('example').then(res => {
...do some stuff...
})
}).catch(err => {
console.log('1') //Not being run
throw err
})
file2.js
class File2(){
auth() {
...works fine and resolves...
}
search() {
return new Promise((resolve, reject) => {
googleapi.somemethod(options, (err, res) => {
if(err) {
console.log('2') // DOES run
reject(new Error(err))
}
resolve(res.field) //Program crashes here because reject didn't actually reject
})
})
}
The call to auth is working just fine, but the call to search (and more specifically googleapi.somemethod) is failing, and err is defined. I check for err, and console.log('2') runs, but then console.log('1') in catch doesn't run, the error isn't thrown, and the program crashed on resolve(res) because res is undefined. I've tried putting the error catcher as the second argument to then instead of using catch, but that still doesn't work
api.search('example').then(res => {
...do some stuff...
}, err => {
console.log('2') // Still doesn't run
throw err
})
I'm running Node v6.2.1
You should return the promise:
var File2 = require('file2')
var api = new File2()
api.auth().then(auth => {
return api.search('example').then(res => { // return the promise
return ...
})
}).catch(err => {
console.log('1') // Not being run
throw err
})
Also, if you don't need auth inside search then you can unnest those promises:
var File2 = require('file2')
var api = new File2()
api.auth().then(auth => {
return api.search('example')
}).then(res => {
return ...
}).catch(err => {
console.log('1') //Not being run
throw err
})
calling reject() does not stop your program, all codes below will be executed too.
Please update from
if(err) {
console.log('2') // DOES run
reject(new Error(err))
}
resolve(res.field) //Program crashes here because reject didn't actually reject
to
if(err) {
console.log('2') // DOES run
reject(new Error(err))
}
else {
resolve(res.field) //Program crashes here because reject didn't actually reject
}
* update *
or you can shorten your code to
if(err) {
console.log('2') // DOES run
return reject(err) // no need to new Error object
}
resolve(res.field) //Program crashes here because reject didn't actually reject

Timing issue while writing mocha test cases

I am trying to write a test case for one of my REST Apis using mocha.
My Rest api looks like this:
server.route({
method : "DELETE",
path : "/local/{id}",
handler: function (request, reply) {
var id = request.params.id;
return getId(id)
.then(function (result) {
return testFunction(result, id, reply);
})
.catch (function (err) {
reply(400).err;
})
}
function testFunction(result, id, reply) {
return update(id, result)
.then(function (resp) {
reply(resp);
stopSomething(id)
.then(function () {
//do some work
return Bluebird.resolve(data);
})
.catch(function (error) {
//handle error & just log..user does not need to know
//stopSomething is a promise chain which runs in background
});
})
.catch(function (err) {
//handle error and reply to user
});
}
});
To test this I wrote the following test case:
describe("with an valid id", function () {
var stopSomethingStub;
var result
before(function () {
stopSomethingStub = Sinon.stub(stopSomethinbObject, "stopSomething", function () {
return Bluebird.resolve();
});
return new Request("DELETE", "/local/id123").inject(server)
.then(function (data) {
result = data;
});
});
after(function () {
//do clean up of stubs
});
it("deletes id", function () {
expect(result.statusCode).to.equal(200);
expect(stopSomethingStub.called).to.be.true;
//Few more checks
});
}
Right now, the "it" block executes immediately after receiving the 200 Ok response for the DELETE Request. However I would like it to finish the entire promise chain before checking the assertion. stopSOmethingStub.called is shown as false if I keep it as the first expect block. However if I keep it as the last assertion it works. I think this is some timing issue.

Categories

Resources