How to stub Mongoose methods with Sinon to test DAO? - node.js

Here is my Node unit test-case for a DAO method that returns a list of Category objects. Category is the name of my model. However, when I run this code, it is getting stuck.
describe('findAllCategories', function () {
it('should find all categoriess', function () {
var stub = sinon.stub(Category, 'find');
stub.callsFake(() => {
return Promise.resolve(allCat);
});
categoryDao.findAllCategories().then(response => {
assert.lengthOf(response, 1);
})
.catch((error) => {
console.log(error);
assert.isDefined(error);
});
stub.callsFake(() => {
return Promise.reject('');
});
categoryDao.findAllCategories().then(response => {
assert.lengthOf(response, 1);
}).catch((err) => {
console.log(err);
assert.isDefined(err);
});
});
});
Any way around?

Related

How to setup DynamoDB table before all tests and tear it down after all tests?

I have simple Jest case where I need to create the table at the beginning, populate it and then run some tests. After all tests are finished, I need to delete the table in DynamoDB.
I tried with beforeAll, creating the table, then testing, and then with afterAll deleting the table.
Nevertheless, it runs non-deterministically, so the population tries to run before creating the table, or the table tries to be deleted before ever creating it.
My code
beforeAll(() => {
await createTable();
populateTable();
//setupLDAPConnection();
});
afterAll(() => {
deleteTable();
});
describe('The program gets list of AD groups', function () {
it('should get a list of user groups in the Active Directory', async () => {
//const result = await userSyncFunction.handler(undefined);
//expect(result).to.be.an('object');
//console.log('2');
});
});
populateTable = () => {
agents.forEach((agent) => {
docClient.put(agent, (err, data) => {
if (err) {
console.log("Error", err);
} else {
console.log("Agent inserted", data);
}
});
});
};
createTable = async () => {
dynamoDb.createTable(agent_table, (err, data) => {
if (err) {
console.log("Error", err);
} else {
console.log("Table created");
}
});
};
deleteTable = () => {
dynamoDb.deleteTable( { TableName: 'AGENT_LIST' }, function(err, data) {
if (err)
console.log(err, err.stack);
else {
console.log('Table deleted');
}
});
};
Any ideas?
Thank you.
Function inside beforeAll should be async, not createTable, since you want to await there. So, it should look like this:
beforeAll(async () => {
await createTable();
populateTable();
//setupLDAPConnection();
});

How to stub the same method in two test-cases using Sinon?

I am using Node, Mongoose, Sinon, Mocha.
In the DAO layer, I have methods named methodA, methodB. In the service layer, I have servMethodA (calls methodA), servMethodB (calls methodB), servMethodC. Now, servMethodC calls the methodA from DAO and then I have a call to methodB nested in it.
In the test cases for the service layer, I have already stubbed methodA and methodB. How do I stub them again for the test-case for servMethodC?
These are my service methods.
function findLikeByPostIdAndUserId(postId, userId) {
return new Promises((resolve, reject) => {
likeDislikeDao.findLikeByPostIdAndUserId(postId, userId).
then((data) => {
resolve(data);
})
.catch((error) => {
reject(error);
});
});
}
function findDislikeByPostIdAndUserId(postId, userId) {
return new Promises((resolve, reject) => {
likeDislikeDao.findDislikeByPostIdAndUserId(postId, userId).
then((data) => {
resolve(data);
})
.catch((error) => {
reject(error);
});
});
}
function saveLike(like) {
console.log(like);
return new Promises((resolve, reject) => {
console.log(data);
likeDislikeDao.findLikeByPostIdAndUserId(like.postId, like.userId).
then((data) => {
if (!data) {
likeDislikeDao.findDislikeByPostIdAndUserId(like.postId, like.userId).
then((dislikeData) => {
if (!dislikeData) {
likeDislikeDao.saveLike(like).
then((data) => {
resolve(data);
});
}
else {
likeDislikeDao.deleteDislike(dislikeData._id)
.then((data) => {
likeDislikeDao.saveLike(like).
then((data) => {
resolve(data);
});
});
}
});
}
else {
likeDislikeDao.deleteLike(data._id)
.then((data) => {
//likeDislikeDao.saveLike(like).
// then((data) => {
// resolve(data);
// });
resolve(data);
});
}
})
.catch((error) => {
reject(error);
});;
});
}
Here are my individual test cases.
describe('saveLike', function () {
it('should add a like', function () {
var stub = sinontwo.stub(likeDislikeDao, 'saveLike');
stub.withArgs(newLike).callsFake(() => {
return Promise.resolve(newLike);
});
var stubtwo = sinontwo.stub(likeDislikeDao, 'saveDislike');
stubtwo.withArgs(newDislike).callsFake(() => {
return Promise.resolve(newDislike);
});
const stubthree = sinontwo.stub(likeDislikeDao, 'findLikeByPostIdAndUserId');
stubthree.callsFake(() => {
return Promise.resolve(like);
});
const stubfour = sinontwo.stub(likeDislikeDao, 'findDislikeByPostIdAndUserId');
stubfour.callsFake(() => {
return Promise.resolve(dislike);
});
likeDislikeService.saveLike(newLike).then(response => {
console.log('1 -> ');
console.log(response);
assert.length(response, 1);
});
stub.withArgs(like).callsFake(() => {
return Promise.reject('');
});
stubtwo.withArgs(dislike).callsFake(() => {
return Promise.reject('');
});
likeDislikeService.saveLike(like).then(response => {
console.log('2 -> ');
console.log(response);
assert.lengthOf(response, 1);
}).then((err) => {
console.log(err);
assert.isDefined(err);
});
});
});
describe('findLikeByPostIdAndUserId()', function () {
it('should find likes by post id and user id', function () {
likeDislikeService.findLikeByPostIdAndUserId(1,2).then(response => {
assert.length(response, 1);
});
likeDislikeService.findLikeByPostIdAndUserId(1,2).then(response => {
assert.length(response, 1);
}).catch((err) => {
//console.log(err);
assert.isDefined(err);
});
})
});
describe('findDislikeByPostIdAndUserId()', function () {
it('should find dislikes by post id and user id', function () {
likeDislikeService.findDislikeByPostIdAndUserId(1,2).then(response => {
assert.length(response, 1);
});
likeDislikeService.findDislikeByPostIdAndUserId(1,2).then(response => {
assert.length(response, 1);
}).catch((err) => {
console.log(err);
assert.isDefined(err);
});
})
});
Problem is, I am not getting the "reject" part of the find-methods in the coverage. Also, the saveLike method is not being covered properly apart from the 'then' lines.

NodeJS EventEmitter test with Mocha

I have a problem testing ldapjs client search operation. It returns an EventEmitter that you have to make listen for some specific event. I wrapped this operations to promisify it and to define my logic and I would like to unit-test it.
findUser(username) {
return new Promise((resolve, reject) => {
logger.debug('Searching user: ', username);
this.ldapClient.bind(user.name, .user.password, err => {
if (err) return reject(err);
else
this.ldapClient.search(root, {filter: `(cn=${username})`}, (errSearch, resSearch) => {
if (errSearch) return reject(errSearch);
const entries = [];
resSearch.on('searchEntry', entry => entries.push(entry.object));
resSearch.on('searchReference', referral => reject(new Error(`Received search referall: ${referral}`)));
resSearch.on('error', err => reject((err));
resSearch.on('end', result => {
if (result.status === 0 && entries.length === 1) {
return resolve({
cn: entries[0].cn,
objectclass: entries[0].objectclass,
password: entries[0].password
});
} else {
return reject(new Error(`Wrong search result: ${result}`));
}
});
});
});
});
}
I am using mockery and Sinon to replace ldapjs dependency inside my module:
beforeEach(function () {
searchEM = new EventEmitter();
sandbox = sinon.createSandbox();
ldapClientStub = Stubs.getLdapClientStub(sandbox);
ldapClientStub.bind.yields(null);
ldapClientStub.search.withArgs('o=ldap', {filter: `(cn=${findParam})`}).yields(null, searchEM);
mockery.registerMock('ldapjs', Stubs.getLdapStub(ldapClientStub));
mockery.registerAllowable('../src/client');
UserClientCls = require('../src/client').default;
userClient = new UserClientCls(config.get());
});
it('should return user with given username', function (done) {
setTimeout(() => {
searchEM.emit('searchEntry', users[1]);
searchEM.emit('end', {status: 0});
console.log('emitted');
}, 500);
searchEM.on('end', res => console.log(res));
userClient.findUser(findParam)
.then(user => {
user.cn.should.equal(users[1].attributes.cn);
user.objectclass.should.equal(users[1].attributes.objectclass);
user.password.should.equal(users[1].attributes.password);
return done();
})
.catch(err => done(err));
});
The problem is that listeners defined inside findUser are never called (but the function itself is called). The listener I defined in the test (just to debug the behaviour) is correctly called.
I do not understand if I miss something about how EventEmitters works or if I am doing the test in a wrong way. Or maybe I wrote a bad piece of code that cannot be tested.
I found a solution to my problem. I extended the base EventEmitter: I added the logic to store which event I want to emit and overrode its on method with a logic to emit my fake event.
class TestEventEmitter extends EventEmitter {
constructor() {
super();
}
setFakeEmit(fakeEmit) {
this.fakeEmit = fakeEmit;
}
on(eventName, cb) {
super.on(eventName, cb);
if (super.eventNames().length === 4)
this.fakeEmit.forEach(f => this.emit(f.name, f.obj));
}
}
So, in beforeEach I can stub ldapClientStub.search to make it return my TestEventEmitter:
beforeEach(function() {
searchEM = new TestEventEmitter();
searchEM.setFakeEmit([{
name: 'searchEntry',
obj: { object: users[1].attributes }
}, {
name: 'end',
obj: { status: 0 }
}]);
...
ldapClientStub.search.withArgs('o=ldap', { filter: `(&(cn=${findParam})(objectclass=astsUser))` }).yields(null, searchEM);
})
This solution may be not very elegant, but it works. If someone can post a better solution I'll be glad to have a look.

How to properly stub out request-promise in a mocha unit test using sinon?

My unit test is:
describe.only("Validator Service Tests", function () {
let request
before((done) => {
request = sinon.stub()
done()
})
beforeEach(() => {
process.env.API_URL = "http://test"
})
it('Should return with no errors if the file matches the schema', () => {
const updateStatusSpy = sinon.spy(FileLib, 'updateStatus')
request.yields({message: 'ok'})
return ValidatorService.handleMessage({
file: 'test'
})
.then((response) => {
assert()
console.log(response)
sinon.assert.calledOnce(updateStatusSpy)
assert(response, 'f')
})
})
})
The problem is my handleMessage function, which looks like:
exports.handleMessage = (message, done) => {
return stuff()
.then((result) => {
console.log('result', result)
if(result) {
return FileLib.updateStatus(fileId, 'valid')
}
return FileLib.updateStatus(fileId, 'invalid')
})
.then(done)
}
And my updateStatus function:
exports.updateStatus = function(fileId, status) {
console.log(fileId, status)
return request.put({
uri: `${process.env.API_URL}/stuff/${fileId}`,
body: {
status: status
}
})
}
My actual request call is buried so deep in, how can I stub it out when testing?
I'm not sure I completely understand your question, but if you are just trying to stub put, try something like this:
let stub;
beforeEach(() => {
putStub = sinon.stub(request, 'put').resolves('some_val_or_object'); //or yields or callsFake, depending on what you're using
});
it('should call request with put', async () => {
await //call your code
expect(putStub.called).to.be.true;
expect(putStub.calledWith(whatever_you_want_to_check)).to.be.true;
});

Sinon to unit test 'catch' statement

I've got a simple function such as;
module.exports = {
fetchUser:function(myUserId) {
return new Promise((resolve, reject) => {
this.getUser(myUserId)
.then(user => {
// do logic // then return user
return user;
})
.then(resolve)
.catch(err => {
// whoops there has been an error
let error = { error: 'My Error' };
reject(error);
});
});
}
};
I want to unit test both the resolve and reject result.
A simple chai test would be;
var expect = require('chai').expect;
var user = require('./user');
describe('User module', function() {
it('test fetchUser', function() {
let _user = user.fetchUser('abc123');
return _user
.then(user => {
expect(data).to.be.an('object');
});
});
Using sinon or another library, how can I for the fetchUser function to throw that reject error?
With Mocha, Chai and Sinon it can be implemented with stubbed method getUser.
const User = require("./fetchUserModule");
describe('User module', () => {
beforeEach(() => User.getUser = sinon.stub());
afterEach(() => User.getUser.reset());
it('returns user if `getUser` returns data', () => {
const user = {name: 'John'};
User.getUser.withArgs("abc123").returns(Promise.resolve(user));
return User.fetchUser("abc123").then(result => {
expect(result).to.equal(user)
}).catch(error => {
expect(error).to.be.undefined;
})
});
it('throws error if `getUser` is rejected', () => {
User.getUser.withArgs("abc123").returns(Promise.reject());
return User.fetchUser("abc123").then(result => {
expect(result).to.be.undefined;
}).catch(err => {
expect(err).to.eql({error: 'My Error'})
})
});
});
Start with anything in your "logic" that can throw an error.
If not you would need to stub this.getUser to reject or throw an error instead of returning data. sinon-as-promised patches sinon.stub to include the .resolves and .rejects promise helpers.
const sinon = require('sinon')
require('sinon-as-promised')
Setup the stub for the failure tests.
before(function(){
sinon.stub(user, 'getUser').rejects(new Error('whatever'))
})
after(function(){
user.getUser.restore()
})
Then either catch the .fetchUser error or use chai-as-promised for some sugar.
it('test fetchUser', function() {
return user.fetchUser('abc123')
.then(()=> expect.fail('fetchUser should be rejected'))
.catch(err => {
expect(err.message).to.eql('whatever')
})
})
it('test fetchUser', function() {
return expect(user.fetchUser('abc123')).to.be.rejectedWith(Error)
})
or async if you live in the new world
it('test fetchUser', async function() {
try {
await user.fetchUser('abc123')
expect.fail('fetchUser should be rejected'))
} catch(err) {
expect(err.message).to.eql('whatever')
}
})
As a side note, you don't need to wrap something that already returns a promise in new Promise and be careful about losing error information when chaining multiple .catch handlers.
fetchUser: function (myUserId) {
return this.getUser(myUserId)
.then(user => {
//logic
return user
})
.catch(err => {
let error = new Error('My Error')
error.original = err
reject(error)
});
}

Resources