Testing HttpClient call with callFake() - javascript

I am trying to create a spec to test a method in my Angular service that makes a GET request. The difficulty I am having is mocking the method to get it to return an error instead of the response. If I cannot get it to return an error (such a 400 or 500 for example) I cannot provide full code coverage...
Code being tested:
maingrid.service.ts:
async loadAccountListPromise(id: string) {
let queryParams = `?emailAddress=${id}`;
let promise = new Promise((resolve, reject) => {
this.http.get(`${this.baseUrl}` + queryParams, { responseType: 'json' })
.toPromise()
.then(
(data) => {
this.results = this.formatData(data);
resolve(this.results);
},
(err) => {
this.logService.error('loadAccountListPromise() exception:', err);
this.setError(this.message[0], err);
reject('loadAccountListPromise() exception');
}
);
});
return promise;
}
setError(errorMessage: string, errorCode?: string): void {
this._error.next(new NxpError(errorMessage, 'AccountListService',
errorCode));
}
clearError(): void {
this._error.next(null);
}
This is the spec I have attempted to write to mock the method using callFake():
maingrid.service.spec.ts
it('logs and sets a local error for system errors/exceptions', () => {
let id: string = 'ppandya#pershing.com';
let myUrl = 'https://localhost:9999/...';
let queryParams = `?emailAddress=${id}`;
spyOn(httpClient, 'get').and.callFake( loadAccountListPromise( (response) => {
// need to return error here...somehow
}));
spyOn(logService, 'error');
spyOn(maingridService, 'setError');
maingridService.loadAccountListPromise(id);
let request = httpMock.expectOne(myUrl + queryParams);
expect(request.request.method).toEqual('GET');
httpMock.verify();
expect(logService.error).toHaveBeenCalled();
expect(maingridService.setError).toHaveBeenCalled();
});
I am not sure what I need to do to properly mock the loadAcountListPromise() method so that it enters the error block and calls the setError() and logService.error() methods.

Try to use the 'spyOn()' and return a throw like this:
spyOn(httpClient, 'get').and.returnValue(Observable.throw({status: 404}));
//Observable.throw(new Error(`Error: ${error}`));

Related

Async Wait Issue with Service when called out from the Controller in Nodejs

I am currently trying to do a get request in my NodeJS API, get some data and return the modified value.
From what I read in other similar questions is that you cannot just return the modified object but you need to use a callback function or a promise in order to return it. I have a standard MVC pattern where I use a controller, service.
Here is my service:
const rp = require('request-promise');
exports.RequestUserPermissions = async function(role, next) {
try {
await rp('https://api.myjson.com/bins/7jau8').then(response => {
const permissionsResponse = JSON.parse(response);
const filteredPermissions = permissionsResponse.find(function(x) {
return Object.keys(x).indexOf(role) > -1;
});
console.log(filteredPermissions); // I GET UNDEFINED HERE.
return filteredPermissions;
});
} catch (error) {
console.log(error);
next(error);
}
};
Here is my controller:
const UserPermissionsService = require('../services/userPermissions.service');
exports.getUserPermissions = async function(req, res) {
try {
const role = req.headers.role; // console.log(req.headers.role);
const loggedInUserPermissions = await UserPermissionsService.RequestUserPermissions(role);
return res.status(200).json({ status: 200, data: loggedInUserPermissions, message: 'User permissions retrieved.' });
} catch (error) {
throw Error(error, 'error inside the get.user.permissions function');
}
};
So my issue is that I'm trying to return the value of filteredPermissions to my controller but I keep getting undefined. Which I guess it's a async - await issue. Meaning that the function ends before I make my calculations.
I originally had my service as:
await request.get('https://api.myjson.com/bins/7jau8', (error, response, body) => {
if (!error && response.statusCode === 200) {
const permissionsResponse = JSON.parse(body);
const filteredPermissions = permissionsResponse.find(function(x) {
return Object.keys(x).indexOf(role) > -1;
});
return permissionsResponse;
} else {
console.log('Got an error:', error);
}
});
but I changed it to use the request-promise module, so that I can return my response. What am I doing wrong ? How can I pass my calculations properly??
Change this:
await rp('https://api.myjson.com/bins/7jau8')
to this:
return rp('https://api.myjson.com/bins/7jau8')
You need to be returning something useful from your exports.RequestUserPermissions function. As it stands now, there's no return value from that function which means the promise it returns will just have an undefined resolved value which is apparently what you are experiencing.
Then, I'd suggest using a .catch() for the error condition. And, you need to allow the caller to see the error (probably as a rejected promise) so it can know when there's an error.
I would suggest this:
const rp = require('request-promise');
exports.RequestUserPermissions = function(role, next) {
return rp('https://api.myjson.com/bins/7jau8').then(response => {
const permissionsResponse = JSON.parse(response);
const filteredPermissions = permissionsResponse.find(function(x) {
return Object.keys(x).indexOf(role) > -1;
});
console.log(filteredPermissions); // I GET UNDEFINED HERE.
return filteredPermissions;
}).catch(error => {
console.log(error);
next(error);
throw error;
});
};
The spec for exactly what you want is a bit confused. To be able to test things with the URL you gave me, I created a simple stand-alone node program here. This looks for one matching role and returns that. If no matching role is found, it resolves to null. You could also make that reject, depending upon how the caller wants no matching role to work.
const rp = require('request-promise');
function getRole(role) {
return rp({uri: "https://api.myjson.com/bins/7jau8", json: true}).then(data => {
// need to find the matching role
// apparently role can be a string or an array of strings
for (let item of data) {
if (item[role]) {
return item[role];
}
}
return null;
});
}
getRole("admin").then(data => {
console.log(data);
}).catch(err => {
console.log(err);
});
When, I run this, I get this output:
{ static:
[ 'posts:list',
'posts:create',
'posts:edit',
'posts:delete',
'users:get',
'users:getSelf',
'home-page:visit',
'dashboard-page:visit' ]
}
Hopefully, can you take this an modify to fit your other needs.
Note: I'm using the json:true option with rp() so it will parse the JSON response for me automatically.
If you are using async/await with request-promise then you don't need to call .then(), you can just assign your rp call directly to a variable. For example this:
await rp('https://api.myjson.com/bins/7jau8').then(response => {
const permissionsResponse = JSON.parse(response);
const filteredPermissions = permissionsResponse.find(function(x) {
return Object.keys(x).indexOf(role) > -1;
});
console.log(filteredPermissions); // I GET UNDEFINED HERE.
return filteredPermissions;
});
Would become this:
const response = await rp('https://api.myjson.com/bins/7jau8');
const permissionsResponse = JSON.parse(response);
const filteredPermissions = permissionsResponse.find(function(x) {
return Object.keys(x).indexOf(role) > -1;
});
console.log(filteredPermissions); // I GET UNDEFINED HERE.
return filteredPermissions;

How to add a new then after fetch has run

I have a method that runs a fetch request and then saves the result or error like this:
saveTema() {
this.gateway.editTema(this.state.tema)
.then(tema => {
this.setState({
tema,
error: null,
isDirty: false,
});
})
.catch(httpOrOtherError => {
if (httpOrOtherError.status) {
if (httpOrOtherError.status === 400) {
httpOrOtherError.json().then(result => {
const serverValidationfailures =
this.transformValideringsfeil(result.valideringsfeil);
this.setState({
error: {
valideringsfeil: {...serverValidationfailures},
},
showActivationDialog: false,
})
});
} else {
this.setState({
error: {httpError: {status: httpOrOtherError.status, statusText: httpOrOtherError.statusText}},
showActivationDialog: false,
});
}
} else {
this.setState({
error: {fetchReject: {message: httpOrOtherError.message}},
showActivationDialog: false,
})
}
})
}
And this is the fetch request itself:
editTema(tema) {
return fetch(
this.temaUrl(tema.id),
{
method: 'PUT',
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(tema)
})
.then(res => {
if (res.ok) {
return res.json();
}
throw res;
}
);
}
I would like to run this method from another one, and check if everything went ok with this method and based on that do further actions. Something like this:
this.saveTema().then(() => {
this.props.history.push({
pathname: '/tema',
state: {
successMessage: `Tema ${this.state.tema.id} ble oppdatert`,
}
}}));
But, this is of course wrong, I am not sure how can I do this, to run some code after the fetch handling of the fetch request has finished. What is the right way to do it?
saveTema() {
return this.gateway.editTema(this.state.tema)
...
Return the promise and then you'll be able to do exactly what you are trying to do.
Return the editThema result after setting up the handlers:
saveTema() {
let prom = this.gateway.editTema(this.state.tema)
prom.then(tema => {
// .. success handling code
})
.catch(httpOrOtherError => {
// .. error handling code
})
return prom;
}
Now you can call your function exactly like you wanted to.
You can achieve that by two approaches
Using async/await
Using native Promise
1. async/await way
userController.js
const userUtils = require('./userUtils');
const userCtr = {};
userCtr.searchUser = async (req, res) => {
try {
const { userName } = req.query;
const result = await userUtils.searchUser(userName);
return res.status(200).json(result);
} catch (err) {
return res.status(err.code).json({ error: err.error });
}
};
module.exports = userCtr;
userUtils.js
const userUtils = {};
userUtils.searchUser = async (userName) => {
try {
if (userName) {
// ...Do some cool stuff
const result = [];
return result;
}
const errorObj = { code: 400, error: 'ERR_VALID_PARAM' };
throw errorObj;
} catch (err) {
console.error(err);
throw err;
}
};
module.exports = userUtils;
2. Promise way
userController.js
const userUtils = require('./userUtils');
const userCtr = {};
userCtr.searchUser = (req, res) => {
const { userName } = req.query;
userUtils.searchUser(userName)
.then((result) => {
return res.status(200).json(result);
})
.catch((err) => {
return res.status(err.code).json({ error: err.error });
});
};
module.exports = userCtr;
userUtils.js
const userUtils = {};
userUtils.searchUser = (userName) => {
return new Promise((resolve, reject) => {
if (userName) {
// ...Do some cool stuff
const result = [];
return resolve(result);
} else {
const error = { code: 400, error: 'Please provide valid data!' }
return reject(error);
}
});
};
module.exports = userUtils;
In both approaches you can hold further execution (in both approach Promise are used directly or indirectly), In a second approach you can achieve by .then().catch() whereas in the first approach just you need to put a keyword await and put async on your function, I suggest you to use async/await. Because when you need to wait for the completion of more than 3 promises and yo go with Native Promise then your code will be so messy like .then().then().then() Whereas in a first approach you just need to put a keyword await on starting of your function, Using async/await approach your code will neat and clean and easily understandable and easy to debug.

Resolving async value from JS class

In short: I need to request a token from Auth0 in order to then retrieve user information (all inside a Vue plugin that is called by Vuex).
I currently have two classes - one for dealing with the token and the other for actually retrieving the profile.
export class Token {
constructor () {
this.options = { method: 'POST',
url: 'https://.auth0.com/oauth/token',
headers: { 'content-type': 'application/json' },
body: '{"client_id":"","client_secret":"","audience":"","grant_type":"client_credentials"}' }
}
async getToken () {
try {
this.request = request(this.options, function (error, response, body) {
if (error) {
console.error(error)
} else {
let parsed = JSON.parse(body)
let token = parsed.access_token
return token
}
})
let blah = await this.request
console.log(blah)
} catch (err) {
console.error(err)
}
}
}
export default class Profile {
constructor () {
const token = new Token()
let result = token.getToken()
//console.log(result)
this.auth0Manage = new auth0.Management({
domain: '.auth0.com',
token: `${result}`
})
}
getProfile () {
return new Promise((resolve, reject) => {
let idToken = localStorage.getItem('id_token')
let decoded = jwt_decode(idToken)
let userId = decoded.sub
this.auth0Manage.getUser(userId, function (err, data) {
if (err) {
console.error(err)
}
resolve(data)
})
})
}
}
I am expecting the following flow:
Get token > Get profile. It doesn't have to be in two classes (originally it wasn't but I couldn't instantiate the constructor for Profile without getting the token first, which would have to be its own class (afaik). Thanks!
Looks like you are not awaiting your getToken result in the profile constructor. Problem is, constructors are always synchronous. So in order to await the token, you need to do something like make Profile#auth0manage an async value and await that. Maybe like:
export default class Profile {
constructor () {
let token = new Token()
this.auth0Manage = new Promise((resolve, reject) => {
token.getToken().then((result) => {
return new auth0.Management({
domain: '.auth0.com',
token: `${result}`
})
}).then(resolve, reject)
})
}
async getProfile () {
let auth0Manage = await this.auth0Manage
return new Promise((resolve, reject) => {
let idToken = localStorage.getItem('id_token')
let decoded = jwt_decode(idToken)
let userId = decoded.sub
auth0Manage.getUser(userId, function (err, data) {
if (err) {
console.error(err)
}
resolve(data)
})
})
}
}
Caveats of this are that if you get an error in getToken then every call to getProfile will return the same error. So you may want to handle that somehow. Also, you have to remember to await every use of this.auth0Manage. Ideally, you could do something like pass in the auth0Manager to the constructor of Profile, so that you won't event try to make a profile until the token has been fetched. Tends to work out better to do things that way.

Jest test a promise wrapped request

I have a request call wrapped in a promise chain as follows
import * as request from 'request';
public async getData (): Promise<string> {
const token = await this.util.getToken();
const options = {
withCredentials: true,
Connection: 'keep-alive'
};
return new Promise<string>((resolve, reject) => {
request(this._Host, options, (err, data) => {
if (err != null) {
return reject('Fetch Failed');
}
const values = JSON.parse(data.body);
this._result = values.result;
return resolve('Fetch Success');
});
});
}
And I'm using jest to test this method using the following code
it('should fetch data', async(done) => {
const classobj = new ClassName(config);
(classobj as any).util.getToken.// tslint:disable-line
mockResolvedValue('some-token');
const spy = jest.mock('request', (path, options) => {
return Promise.resolve('Fetch Success');
});
const dispatcher = await classobj.getData().then(done());
});
However the code inside request block never gets triggered. But the test is displayed as running successfully as well. I also noticed that if I pass a values that's already resolved within the return new Promise then the code block is getting triggered without any issues. Is there something that's missing in this code to test the request call?

promise.all not access in rejection function

i have one service that get data , and i called it 5 times with different parametes to get different data.
I called a function to execute in success case : it work fine.
but in case of failure one from the 5 calls i need to do something else what's not happen : it always enter in success function.
I'm using ionic 4 angular 2
this is the service i have :
public getdataLookUps(type, lcid): Promise<string[]> {
return new Promise((resolve, reject) => {
if (this.data[type + lcid]) {
resolve(this.data[type + lcid]);
return;
}
this.authService.getToken().then(
(accessToken) => {
let headers = new Headers({'Authorization': 'bearer ' + accessToken});
let url = 'error url to test failure case';
this.http.get(url, {headers: headers})
.map(res => res.json())
.toPromise()
.then(
(res) => {
this.data[type + lcid] = res;
resolve(res);
},
(error) => {
reject(error);
}
);
}
);
});
}
then I wrapper the function that calls the service like this: ( repeated 5 times with different params):
public getAreas() {
return this.lookupsService.getdataLookUps('Region', this.lcid).then(
(res) => {
this.areas = res;
},
() => {
//todo
return Promise.reject('rejection error');
}
);
}
then I call the 5 functions :
ngOnInit() {
this.getCaseCategories();
this.getAreas();
this.getWeather();
this.getMifonProjects();
this.getUserInfo();
}
and I do promise.all() here :
ngAfterViewInit(){
Promise.all(
[
this.getCaseCategories(),
this.getAreas(),
this.getWeather(),
this.getMifonProjects(),
this.getUserInfo(),
]
).then(
() => {
this.loadMap();
},
() => {
this.showErrorMessage = true;
}
);
}
This code has two callbacks for then, a success handler, and an error handler. If the code is as you have shown the error handler returns a success result so your Promise.all() will always succeed:
public getAreas() {
return this.lookupsService.getdataLookUps('Region', this.lcid).then(
(res) => {
this.areas = res;
},
() => {
//todo
}
);
}
Don't add an error handler unless you are really able to handle the error here. Instead just let the error propagate out to the next handler:
public getAreas() {
return this.lookupsService.getdataLookUps('Region', this.lcid)
.then(res => this.areas = res);
}
Now your Promise.all will give you an error when the data lookup fails.
Also stop nesting your promise handlers:
public getdataLookUps(type, lcid): Promise<string[]> {
if (this.data[type + lcid]) return Promise.resolve(this.data[type + lcid]);
return this.authService.getToken().then(
(accessToken) => {
let headers = new Headers({'Authorization': 'bearer ' + accessToken});
let url = 'error url to test failure case';
return this.http.get(url, {headers: headers})
.map(res => res.json())
.toPromise();
})
.then((res) => this.data[type + lcid] = res);
}
Once you have a Promise just return the Promise there is no need to create a new Promise. And if your promise success handler creates another promise return that to avoid nesting. Your error handler did nothing but propagate the error, so when you don't have the nested promise you don't need that either, just let the error propagate naturally.
I solved it by removing the calls of the functions in ngOnInit();
and keep everything same as my example above (not change anything in the getDataLookUps service)

Categories