Parameter don't pass in chai - node.js

I'm try to test my PUT route through chai put the data I send through chai doesn't get passed properly into the function. When I used console.log to check the value of loanID it comes as undefined. I'm not sure its because I'm using an incorrect way to pass the methods. The route works fine when tested using Postman.
Mocha-Chai test:
describe('PUT /loanApproval', () => {
it('it should give approval to loans', (done) => {
const loan = new Loan({ loanID : 1,
manager : 'John Doe',
status : 'Approve'});
loan.save((err, loan) => {
chai.request(server)
.put('/loanApproval')
.send(loan)
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a('object');
res.body.should.have.property('result');
res.body.result.should.have.property('loanID').eql(1);
res.body.result.should.have.property('manager');
res.body.result.should.have.property('status');
done();
});
});
});
});
Function:
//loan aproval
this.loanApproval = function(req,res) {
const loanID = req.params.loanID;
//geting existing details from loan
Loan.findOne({ 'loanID' : loanID }, function(err, loan) {
if(err) {
console.log(err);
return res.send({'error':err});
}
else if (!loan) {
// If loan doesn't exist i.e. the wrong loanID was given
req.log.error('Loan does not exist to update: ', loanID);
return res.json({'error':'Record does not exist'});
}
//update deteails
loan.manager = req.params.manager;
loan.status = req.params.status;
//send data to database
loan.save(function(err, result) {
if(err) {
console.log(err);
return res.send({'error':err});
}
else {
return res.json({'Aproval details':result});
}
});
});
};
Error:
Loan PUT /loanApproval it should give approval to loans:
Uncaught AssertionError: expected { error: 'Record does not exist' } to
have a property 'result'
at chai.request.put.send.end (test\loan.js:261:46)
at Test.Request.callback (node_modules\superagent\lib\node\index.js:619:12)
at node_modules\superagent\lib\node\index.js:795:18
at IncomingMessage.<anonymous>
(node_modules\superagent\lib\node\parsers\json.js:16:7)
at endReadableNT (_stream_readable.js:975:12)
at _combinedTickCallback (internal/process/next_tick.js:80:11)
at process._tickDomainCallback (internal/process/next_tick.js:128:9)

Your route handler function is using req.params, which is meant to be used for named route parameters (routes that are declared with :xxx placeholders).
Your test suggests that instead of using req.params, you should be using req.body.
I'm not sure why it would work in Postman, like you state.

Related

how can I test fail error function with chai and mocha

Hello guys i'm newbie with testing
I'm using socket.io and I want to simulate throwing error by my function when something happens on the insertion.
socket.on('request', function(request) {
bookingService.addBooking(request)
.then(function (booking) {
winston.info('Client Order saved');
io.emit('order to driver', {user: request[2], random : request[3]});
})
.catch(function (err) {
winston.error(' Client error on save order ==> '+err);
});
});
addBooking
function addBooking(msgParam) {
var deferred = Q.defer();
db.booking.insert(
{ user : msgParam[2],
adress : msgParam[0],
random : msgParam[3],
driver : [],
isTaken : false,
isDone : false,
declineNbr : 0 ,
createdOn : msgParam[1],
createdBy : msgParam[2],
updatedOn : null,
updatedBy : []},
function (err, doc) {
if (err) deferred.reject(err.name + ': ' + err.message);
deferred.resolve();
});
return deferred.promise;
}
I tried to just test the addBokking function
it('should throw error if something wrong happend on adding new order ', function(done){
(bookingService.addBooking(request)).should.throw()
done();
});
but I get this error
AssertionError: expected { state: 'pending' } to be a function
You can use the following syntax with chai:
it("throw test", () => {
expect(()=>{myMethodThatWillThrowError()}).to.throw();
});
For promises, you can use the following pattern:
it("should throw on unsuccessfull request", (done: MochaDone) => {
repo.patch({
idOrPath: "Root/Sites/Default_Site",
content: ConstantContent.PORTAL_ROOT,
}).then(() => {
done("Should throw"); // The test will fail with the "Should throw" error
}).catch(() => {
done(); // The test will report success
});
});
You are checking the promise and not the result
This error:
AssertionError: expected { state: 'pending' } to be a function
... means you are checking the promise returned from the addBooking function and not the resolved/rejected result of the promise.
With chai-as-promised you can do that easily!
With chai-as-promised these should work for example (from the documentation):
return promise.should.be.rejected;
return promise.should.be.rejectedWith(Error); // other variants of Chai's `throw` assertion work too.
or in your specific case (after installing and connecting chai-as-promised), this should work:
(bookingService.addBooking(request)).should.be.rejected
(maybe should.throw() will work with chai-as-promised too, I'm less familiar with it)
Check it out here: chai-as-promised

How can I test my asynchronous code in a restify HTTP handler?

I have a function in my Restify project that handles an HTTP GET request. After some processing it uses Sequelize to find the user entity for my current session. The User.findOne function returns a promise and depending on the result of that promise, I'm sending an HTTP response with 200 or 404.
static getMe(req, res, next) {
const userInfo = BaseController.getUserSession(req);
// hard to test this part
User.findOne({
where: {email: userInfo.email}
}).then(function(user) {
if (user) BaseController.respondWith200(res, user);
else BaseController.respondWith404(res, 'User not found.');
}, function(error) {
BaseController.respondWith404(res, error);
}).then(function() {
return next();
});
}
I've tried a few different libraries to help with testing so I'm sorry if this is a messy combination of things. This is in my beforeEach function for my tests:
const usersFixture = [
{id:2, email:'ozzy#osbourne.com', facebookId:54321, displayName: 'Ozzy Osbourne'},
{id:3, email:'zakk#wylde.com', facebookId:34521, displayName: 'Zakk Wylde'},
{id:4, email:'john#lennon.com', facebookId:12453, displayName: 'John Lennon'}
];
this.findOneSpy = sinon.spy(function(queryObj) {
return new Promise(function(resolve, reject) {
const user = usersFixture.find(function(el) { return el.email === queryObj.where.email });
if (user) resolve(user);
else resolve(null);
});
});
this.respondWith200Spy = sinon.spy(function(res, data) {});
this.respondWith400Spy = sinon.spy(function(res, error) {});
this.respondWith404Spy = sinon.spy(function(res, error) {});
this.controller = proxyquire('../../controllers/user-controller', {
'../models/user': {
findOne: this.findOneSpy
},
'./base-controller': {
respondWith200: this.respondWith200Spy,
respondWith400: this.respondWith400Spy,
respondWith404: this.respondWith404Spy
}
});
And here is what one of my tests looks like:
it('should return 200 with user data if user email matches existing user', function() {
// THIS FUNCTION IS NEVER HIT
this.respondWith200Spy = function(res, data) {
data.should.equal({id:4, email:'john#lennon.com', facebookId:12453, displayName: 'John Lennon'});
done();
};
const req = {session:{user:{email:'john#lennon.com'}}};
this.controller.getMe(req, this.res, this.nextSpy);
this.findOneSpy.should.have.been.called;
});
Since we aren't actually passing a callback to the function and the function doesn't really return anything (just does asynchronous things elsewhere), I can't figure out how to test it to make sure it's working right. Any help is appreciated.
The actual code works just fine. I'm just trying to get some quality unit testing into the project. Thanks!
I ended up finding a way to do it using proxyquire. I just re-stubbed the controller class that I am testing and made the respondWith200 callback make an assertion. Then I created a new spy for the next function that just calls done (which is passed into the test case). I verified that the code is all getting hit.
it('should return 200 with user data if user email matches existing user', function(done) {
const controller = proxyquire('../../controllers/user-controller', {
'../models/user': {
findOne: this.findOneSpy
},
'./base-controller': {
respondWith200: function(res, data) {
data.displayName.should.equal('John Lennon');
},
respondWith400: this.respondWith400Spy,
respondWith404: this.respondWith404Spy
}
});
const req = {grft_session:{user:{email:'john#lennon.com'}}};
const nextSpy = sinon.spy(function() {
done();
});
controller.getMe(req, this.res, nextSpy);
this.findOneSpy.should.have.been.called;
});

unit testing async-waterfall using mocha

I am using Mocha as a testing framework for my node.js application.
I have an existing module that uses async-waterfall node module.I am trying to write a unit test case for it. I am unable to write a test case for the second function in the array. Any tips on how would I pass the result of one function to the next function in the array
var async = require('async');
module.exports.resetPassword = function(req,res,next){
async.waterfall([
function(done){
var token = do something;
done(err,token);
},
function(token,done){
//do some stuff
res.status(200).send('success');
},
]function(err){
//send failure response
});
};
I think I understand the issue now.
In your test - you execute the line:
user.resetPassword(request,response,next);
And immediately after that - the line:
cryptoStub.restore();
The issue is - that async.waterfall will start the first method, but before it runs the second one, it will run the next line from the unit test, and therefore you don`t see the next method. Code that will solve it will be like:
user.resetPassword(request,response,function(err) {
cryptoStub.restore();
done();
});
Does it look better?
My code is as follows. My issue is around writing a unit test case for this method. I am using mocha along with sinon for stubbing. The unit test that I have written is
Unit Test
it("Should return an error if there is an error in retrieving the user details from the db",function(done)
{
request={
body:{
email:'test#test.com'
}
};
var expectedMessageFromCrypto = '12121212121';
var cryptoStub = sinon.stub(crypto, "randomBytes",function(err,callback){
callback(null,expectedMessageFromCrypto);
});
user.resetPassword(request,response,next);
cryptoStub.restore();
done();
});
});
The resetPassword module
methods.exports.resetPassword = function (req, res, next) {
var token = null;
async.waterfall([
function (done) {
crypto.randomBytes(16, function (err, buf) {
if(err){
return res.status(400);
}
else{
token = buf.toString('hex');
}
done(err, token);
});
},
function (token, done) {
var user = getUserInformation(req.body.email);
sendEmail(user, done);
}
], function (err) {
if (err) {
logger.error('error in sending confirmation mail');
return next(err);
}
res.status(200).send('success');
});
};

Mongoose findById possibly sending Express headers?

I am trying to send an error if a condition is true using the Mongoose function findById. The problem is that Mongoose appears to be setting the res Express object and is then throwing an error when I try to set the headers myself. Here is the code:
console.log(res.headersSent); // false
Trade.findById(req.body.trade, function (err, trade) {
if (err) throw err;
// Ensure user is not making an offer on their own item
Item.findById(trade.listing, function (err, item) {
if (err) throw err;
if (req.decodedId == item.user) {
console.log(res.headersSent); // true (?)
return res.status(403).send({
success: false,
message: 'You cannot make an offer on your own item'
})
} else {
return;
}
})
And here is the stack trace for the error:
false // res.headersSent() before calling Trade.findById()
POST /api/v2/offer 200 148.799 ms - 162
true // res.headersSent() after calling Item.findById() and checking error condition
_http_outgoing.js:335
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:335:11)
at ServerResponse.header (/Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/express/lib/response.js:700:10)
at ServerResponse.send (/Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/express/lib/response.js:154:12)
at ServerResponse.json (/Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/express/lib/response.js:240:15)
at ServerResponse.send (/Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/express/lib/response.js:142:21)
at /Users/Matt/Dropbox/work/TradeRate/prototype/server/controllers/offers.js:48:40 // LINE THAT CONTAINS return res.status(403).send ...
at /Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/mongoose/lib/query.js:1169:16
at /Users/Matt/Dropbox/work/TradeRate/prototype/node_modules/mongoose/node_modules/kareem/index.js:103:16
at process._tickCallback (node.js:355:11)
18 Jul 15:26:39 - [nodemon] app crashed - waiting for file changes before starting...
What could be causing this error? Is there aspect of the Mongoose API that sets the response headers that I'm missing?
EDIT: I added my full (updated) exported route handler in case that has some context that would make the problem more clear.
// POST /api/offer
exports.createOffer = function (req, res, next) {
console.log(res.headersSent);
Trade.findById(req.body.trade, function (err, trade) {
if (err) {
next(err);
return;
} // not good to throw from async events, let express' error handling middleware take care of it
// Ensure user is not making an offer on their own item
Item.findById(trade.listing, function (err, item) {
if (err) {
next(err);
return;
}
if (req.decodedId == item.user) {
console.log(res.headersSent); // true (?)
res.status(403).send({
success: false,
message: 'You cannot make an offer on your own item'
});
}
// all done with async stuff, pass the request long
next();
});
// If trade is expired, reject the offer
if (trade.expiresOn < Date.now()) {
res.status(403).send({
success: false,
message: 'This trade has expired and cannot accept new offers'
});
}
// Create new offer and add data
var newOffer = new Offer();
newOffer.items = req.body.items;
newOffer.trade = req.body.trade;
newOffer.save(function (err, offer) {
if (err) throw err;
});
// Add offer to items in offer
for (var i = 0; i < req.body.items.length; i++) {
Item.findById(req.body.items[i], function (err, item) {
if (err) throw err;
item.offers.push(newOffer._id);
item.save(function (err, item) {
if (err) throw err;
});
});
}
// Add offer to trade
trade.offers.push(newOffer._id);
trade.save(function (err, trade) {
if (err) throw err;
});
return res.send(newOffer);
});
};
Completely new answer, disregard my old one it was all wrong (it's been a while since I've used express).
Anyway the problem is you're calling async functions which return immediately so at the bottom there when you're calling return res.send(newOffer);, you're doing it before any of those callbacks return. So you returned before you
Check if the user is trying to create an offer on their own item
Add the new offer id to the items
Save any of those changes
Another problem is your loop there will likely fail horribly. There's no guarantee that you'll be pushing those items in order because findById and save as async, they return instantly and may be executed in any order. Plus there's no reason at all to save after every push. You need to either wait for each findById to return before continuing the loop (so you can't use a basic for loop, most likely a callback) or more correctly, just use a mongoose update query to do this all at once (you don't need to load an item to push an offer to it, just use $push)
The best way to handle all of this in express is with middleware. So change your code to this (I've added a dependency on http-errors to make error handling easier.
I'm assuming you're using the most recent version of express:
The Offer Route
var httpError = require('http-error') // needed for ezpz http errors
var express = require('express'); // needed for express.Router()
// middleware that loads the trade
function loadTrade(req, res, next) {
Trade.findById(req.body.trade, function (err, trade) {
req.trade = trade;
next(err, trade);
})
}
// middlware that checks expiration
function checkExpired(req, res, next) {
if(req.trade.expiresOn < Date.now())
next(httpError(403, 'This trade has expired and cannot accept new offers'));
else next();
}
// middleware makes sure the user isn't making an offer on their own item
function checkIsOwner(req, res, next) {
Trade.findById(req.trade.listing)
.select('user')
.exec(function(err, listing) {
if (err) next(err)
else if (listing.user == req.decodedId) next(httpError(403, 'You can not make an offer on your own item'))
else next();
})
}
// now we can create an offer
function createOffer(req, res, next) {
// req.trade was loaded and validated by our middleware
// if next(err) was called at any point this function wouldn't be called
var trade = req.trade;
Offer.create({trade: trade._id, items: req.body.items}, function (err, offer) {
if (err) {
next(err); // we only call next to trigger the error handler
return;
}
// now push the new offer id to all the items
Item.update({$in: req.body.items}, {$push: offer._id}, function (err, offer) {
if (err) next(err)
else res.json(newOffer);
})
});
}
exports.createOffer = express.Router()
.post(loadTrade)
.post(checkExpired)
.post(checkIsOwner)
.post(createOffer);
For handling errors I'd add this after you've setup all the routes (where you have your app.post('/api/v2/offer', ....) stuff:
app.use('/api/v2/*', function(err, req, res, next) {
res.status(err.status || 500).json({ success: false, message: err.message });
});
Now whenever you call next(err), this error handler will be called and send a status code and error message.

Obtain data from a GET in superagent function

I'm trying to do something like this:
var countElemnts = function() {
superagent
.get('/someOtherUrl')
.query({
type: value.type
})
.end(function(err, res) {
console.log(JSON.stringify(res.body));
if (res.ok) {
reply(JSON.stringify(res.body));
} else {
console.log(err);
}
})
};
superagent
.post('/someUrl')
.type('json')
.send({
name: 'value.update',
data: {
id: request.params.id,
type: value.type,
count: countElemnts()
}
})
.end(function() {
reply({
message: 'ok'
});
});
In data option of the send function I'm trying to call a function to obtain some value.
What I want is to get the value that comes in the body of a reply, ie the res.body. Upon console.log get this [{ "count ": 3 } ], but if I do a console.log of res.body.count tells me undefined, what could I do to get the value 3.
Thanks.
Since the return doesn't have the extra space in "count " (as mentioned in the comments) the problem was that you were trying to access the count attribute of the array and not the object (first element of the array), so to access it you should do it like:
res.body[0].count
As for the problem of not being able to get the count in your POST, the problem is that countElemnts uses an asynchronous function. The end method of superagent takes a function as parameter and it is only called when it receives a response. By that time, your function had already returned (with undefined, since you didn't return anything).
You should first make the GET call and then send it to a function that will handle the POST. For example:
superagent
.get('/someOtherUrl')
.query({
type: value.type
})
.end(function(err, res) {
if (err) {
console.log(err);
} else {
console.log(JSON.stringify(res.body));
sendCount(res.body[0].count)
}
});
function sendCount(count) {
superagent
.post('/someUrl')
.type('json')
.send({
name: 'value.update',
data: {
//id: request.params.id, // not sure where you are getting these values from,
//type: value.type, // but you should adapt it to your code
count: count
}
})
.end(function() {
reply({
message: 'ok'
});
});
}

Resources