how to handle catch in sinon, unit testing nodejs - node.js

in my UserService:
createUser: async (data) => {
const user = new UserDBEntryMapper(data);
const createdUser = await UserModel.create(user).catch(error => {
this.handleError(error);
});
return createdUser ? new UserBOMapper(createdUser) : null;
}
in my user.test.js
it.only('Create New User', async () => {
const stub = sinon.stub(UserModel, "create").returns(user);
const created = await UserService.createUser(user);
expect(stub.calledOnce).to.be.true;
});
throwing the error as:
Create New User:
TypeError: UserModel.create(...).catch is not a function
If I remove catch block in UserService the test passes, but we need a catch block there. How do I handle this?
Note: UserModel.create() is sequelize default function

Promise needs to be returned in a mock. Something like this
const stub = sinon.stub(UserModel, "create").returns(Promise.resolve(user));

Related

Callable cloud functions - handle error in android

im trying to delete an user from firestore and from auth.
I have this callable cloud function:
export const deleteUser = functions.https.onCall(async (data, context) => {
const userEmail = data.userEmail;
const collection = data.collection;
try {
deleteUserByEmail(userEmail, collection)
return "deleted!"
} catch (error) {
throw new functions.https.HttpsError('invalid-argument', 'there is no user with that email', error);
}
})
async function deleteUserByEmail(userEmail: string, collection: string) {
const auth = admin.auth();
const db = admin.firestore();
const { uid } = await auth.getUserByEmail(userEmail);
await db.collection(collection)
.doc(uid)
.delete();
await auth.deleteUser(uid);
return uid;
}
in android i have this:
fun deleteFromFirebase(){
val data = hashMapOf(
"userEmail" to user.email,
"collection" to "User"
)
functions // Optional region: .getInstance("europe-west1")
.getHttpsCallable("deleteUser")
.call(data)
.addOnCompleteListener() { task ->
if(!task.isSuccessful)
{
Log.d("User", "ERROR")
val e = task.exception
if (e != null) {
Log.d("Admin", e.message.toString())
}
}else{
Log.d("User", "Deleted")
//make something
}
}
}
If the user in auth and the document nin firestore exist, works great.
But i tryed to generate some error.
So I deleted the user from auth and ran the function. The Android log says D/User: User deleted
but in the console from google cloud:
Function execution took 1878 ms, finished with status code: 200
Exception from a finished function: Error: There is no user record corresponding to the provided identifier.
How can I handle the error and get correctly in android? Thanks!
The deleteUserByEmail function is async and returns a Promise. Your return statement runs before the promises is resolved. Try refactoring the code as shown below:
export const deleteUser = functions.https.onCall(async (data, context) => {
const userEmail = data.userEmail;
const collection = data.collection;
try {
// add await, continues after Promise is resolved
await deleteUserByEmail(userEmail, collection)
return "deleted!"
} catch (error) {
console.log(error) // <-- check for any errors
throw new functions.https.HttpsError('invalid-argument', 'there is no user with that email', error);
}
})
async function deleteUserByEmail(userEmail: string, collection: string) {
const auth = admin.auth();
const db = admin.firestore();
const { uid } = await auth.getUserByEmail(userEmail);
return await Promise.all([
db.collection(collection).doc(uid).delete(),
auth.deleteUser(uid)
])
}

Jest Mock function is not getting called

I have a node script which goes on like
const { instance } = new SDK(id, authToken);
const data = await getAllModels(instance); // helper method which uses the sdk instance to return all models
items = await getItem(instance, id);
I have abstracted getAllModels and getItem into a helper module inside helper.js
exports.getAllModels = async (instance) => {
const { data } = await instance.getModels();
return data;
};
exports.getItem = async (instance, zuid) => {
const items = await instance.getItems(zuid);
return items;
};
I am trying to mock both the functions in my test so that I can expect the values based on my values.
jest.spyOn(helper, 'getAllModels').mockImplementation(() => {
console.log('Test');
return Promise.resolve('c');
});
console.log('Test');
jest.spyOn(helper, 'getItem').mockImplementation(() => {
console.log('Test 1');
return Promise.resolve('d');
});
const baseVal = await main(instance, token);
expect(baseVal).toBe("some value");
I can see that the mock values are not getting called and instead a direct call to the script is being used, what am I missing ?
From what I can see from your code, getAllModels and getItem are named exports from helper.js, which you can see from the use case you posted in your first code block.
So in your test file you could have something like the following:
const { getAllModels, getItem } = require('./helper');
jest.mock('./helper', () => {
return {
getAllModels: jest.fn(() => {
console.log('Test');
return Promise.resolve('c');
}),
getItem: jest.fn(() => {
console.log('Test 1');
return Promise.resolve('d');
}),
};
});
I think this is a cleaner implementation than using spyOn in this instance.

Cannot get the result of an async function in controller

This is my controller:
const rssService = require('../services/rss.service');
async function parser() {
const result = await rssService.rssParser('someurl');
return result;
};
const parse = async function (req, res) {
const p = new Promise((resolve, reject) => {
const t = parser();
if (t === undefined) {
resolve(t);
} else {
// eslint-disable-next-line prefer-promise-reject-errors
reject('something bad happened');
}
});
p.then((result) => res.send(result)).catch((message) => console.log(`ERROR ${message}`));
};
module.exports = {
parse,
};
in the function : parser() above, I am trying to call my rss.service.js file which I have the logic. This file is a rss parser which tries to parse the feed and do some calculations (which needs promises and async) and then return the json object.
Here is how my rss.service look :
const rssParser = async function parseRssFeed(url) {
const parser = new Parser();
const appRoot = process.env.PWD;
const downloadDir = `${appRoot}/downloads/`;
if (!fs.existsSync(downloadDir)) {
fs.mkdirSync(downloadDir);
}
try {
const feed = await parser.parseURL('someurl');
const processedFeedItems = await Promise.all(feed.items.map(async (currentItem) => {
const {
fileUrl,
downloadPath,
} = await downloadFile(currentItem);
const hashValue = calculateHash(downloadPath)
return {
title: currentItem.title,
hash: hashValue,
url: mp3FileUrl,
};
}));
return (JSON.stringify(processedFeedItems));
} catch (error) {
console.error(error);
return 'error';
}
};
when I debug my code I can verify that Json object has been created with correct data, but the result does not return to the callee(controller).
I'll go in a little deeper since you mentioned you're new:
const rssService = require('../services/rss.service');
// This is an async function (always returns a promise)
async function parser() {
const result = await rssService.rssParser('someurl');
return result;
};
const parse = async function (req, res, next) {
// In await/async, you should use try/catch/throw instead of .then and .catch
// It does the same thing, but it's the preferred syntax and is "easier" to read IMO
// Out in "the community" people will complain if you mix await/async with promises like that
try {
// Adding await to ensure left-assign works.
// This is necessary because parser is an async function (so returns a promise)
const result = await parser();
// here, since you used `await` you get the value instead of the promise
if (result === undefined) throw new Error('something bad happened')
return res.send(result)
} catch (error) {
console.log(`ERROR ${error.message}`;
// Do you need to do anything else with this error? I assume something like:
return next(error);
}
};
module.exports = {
parse,
};
In a fast look, It seems you have forgot to wait for resolve the parser promise.
...
const p = new Promise(async(resolve, reject) => {
const t = await parser();
...

Jest Unit Test for Node Service having async methods

I'm getting below errors. Why I'm receiving response as undefined from the service?
Is there anything wrong I did for providing mock implementations?
Service:
export class SaveDataService{
async save() : Promise<any> {
try{
return this.someFunction()
} catch(ex){
throw new Error('some error occured')
}
}
async someFunction() : Promise<any>{
const response = {
"file" : "<htm><body>This is sample response</body></html>"
}
return Promise.resolve(response);
}
}
Test/Spec file:
import { SaveDataService } from "./save-data.service";
jest.mock('./save-data.service')
describe('tests for SaveDataService', () => {
it('when save method is called and success result is returned', async () => {
let mockSaveDataServiceSomeFunction = jest.fn().mockImplementation(() => {
return Promise.resolve('Success Result')
});
SaveDataService.prototype.someFunction = mockSaveDataServiceSomeFunction;
let spy = jest.spyOn(SaveDataService.prototype, 'someFunction');
let service = new SaveDataService();
let data = await service.save()
expect(data).toEqual('Success Result')
expect(spy).toHaveBeenCalled()
})
it('when save method is called and error is returned', async () => {
let mockSaveDataServiceSomeFunction = jest.fn().mockImplementation(() => {
throw new Error('ERROR')
});
SaveDataService.prototype.someFunction = mockSaveDataServiceSomeFunction;
let spy = jest.spyOn(SaveDataService.prototype, 'save');
let service = new SaveDataService();
service.save()
expect(spy).toThrowError('ERROR')
})
})
A mock replaces the dependency. You set expectations on calls to the dependent object, set the exact return values it should give you to perform the test you want, and/or what exceptions to throw so that you can test your exception handling code.
In this scenario, you are mocking save-data.service by calling jest.mock('./save-data.service'). So that your class may looks like this:
async save() : Promise<any> {
// do nothing or undefined
}
async someFunction() : Promise<any> {
// do nothing or undefined
}
So you must implement the body yourself to expect what exactly you want the method/function to do for you. You are mocking only the someFunction:
...
let mockSaveDataServiceSomeFunction = jest.fn().mockImplementation(() => {
return Promise.resolve('Success Result')
});
SaveDataService.prototype.someFunction = mockSaveDataServiceSomeFunction;
...
So when you call the save() method you still get nothing/undefined.
You are overwriting the whole behavior of the service that I think your test may not be useful. But you can fix your test this way:
import { SaveDataService } from "./save-data.service";
jest.mock('./save-data.service');
describe('tests for SaveDataService', () => {
beforeEach(() => {
SaveDataService.mockClear();
});
it('when save method is called and success result is returned', async () => {
const spy = jest
.spyOn(SaveDataService.prototype, 'save')
.mockImplementation(async () => Promise.resolve('Success Result'));
const service = new SaveDataService();
const data = await service.save();
expect(data).toEqual('Success Result');
expect(spy).toHaveBeenCalled();
})
it('when save method is called and error is returned', async () => {
const spy = jest
.spyOn(SaveDataService.prototype, 'save')
.mockImplementation(() => {
throw new Error('ERROR');
});
const service = new SaveDataService();
expect(service.save).toThrowError('ERROR');
expect(spy).toHaveBeenCalled();
});
});

Unit test with promise handling - node.js

i want to do a unit test with async function on the code. and here is my code on user.test.js
'use strict'
const UserDomain = require("../../../../../../bin/modules/users/repositories/commands/domain")
const UserHandler = require("../../../../../../bin/modules/users/repositories/commands/command_handler")
const expect = require('chai').expect;
const assert = require('chai').assert;
const sinon = require('sinon');
describe('User domain', () => {
describe('".login(data)"', () => {
let user;
beforeEach( () => {
user = {
clientId : "adithyavisnu",
clientSecret : "secretOfmine#19"
}
});
it("should return error when username/password is empty", (done)=> {
done();
// let
})
it("should return object", async () => {
const domainStub = sinon.stub(UserDomain.prototype, 'login');
const result = await UserHandler.login(user);
sinon.assert.calledOnce(domainStub);
domainStub.restore();
})
});
});
If the normal code (not the unit test code above) the const result = await UserHandler.login(user); will have an object response, but when i do in user.test.js it do not get the response. the result is undefined.
here are the user_handler code
'use strict';
const User = require('./domain');
const login = async (data) => {
const postData = async () => {
const user = new User();
const result = await user.login(data);
return result;
}
const response = await postData();
return response;
}
Is there something i did wrong on the code or some code is missing?
I am sorry if you do think there is unclear information
Thank you for the responses
In the normal flow, the UserHandler calls the Domain.login method and returns the result object. When you run the unit test you are stubbing the Domain.login method. so, it wont return the result as normal flow. You can either make the stub return some result object and test that or just spy the Domain.login instead of stubbing it , if you just want to just check that the Domain.login was called without altering its behavior. Read more on stubs/spies here if you would like - http://sinonjs.org/releases/v1.17.7/stubs/

Resources