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
Related
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.
I'm running some tests using Mocha and chai. One of the tests is hanging at a .should.be.deep.equal call.
Here's the test code:
// Make the fake connection before running tests
before(function(done) {
mongoose.connect('mongodb://fake.test/TestingDB', function(err) {
done(err);
});
});
// Test Cases
describe('Testing the functions that deal with users and locations:', function() {
// Test Setup
var req = {};
beforeEach(function(done) {
mockgoose.reset()
async.parallel([function(callback){
sensors.create(testData.deviceData, function(err, model) {
if (err) {console.log(err)}
callback();
});
}, function(callback) {
locations.create(testData.locationData, function(err, model) {
if (err) {console.log(err)}
callback();
});
}], function(err) {
done();
});
});
afterEach(function(done) {
mockgoose.reset();
done();
});
// Tests
describe('function locationList', function() {
it('should list the test location', function(done) {
dbFunctions.locationList(req, function(result) {
console.log(result) //Prints the whole result
console.log(testData.locationList)
result.should.exist; //This doesn't cause it to hang
result.should.be.deep.equal(testData.locationList) //hangs here
done(result);
});
})
})
});
And here's the function it's testing:
exports.locationList = function(req, callback) {
listLocations().then(
function(data) {
callback(data);
},
function(err) {
console.log('Error Retrieving Location Information: ' + err);
callback(err);
});
};
As I note in the comments, the results object exists and gets printed to the console. results.should.exist; doesn't throw an exception and if I comment out everything but it the test works fine. For some weird reason, despite both the testData.locationList and result object existing, the test times out. I have 14 other tests that use the exact same syntax without any problems. Does anyone know what could be causing this to happen for this specific test?
Here's the output from the tests:
Testing the functions that deal with users and locations:
function locationList
[ { devices: {},
address: '123 Fake St, Waterloo, On',
location: 'Unittest',
owner: 'unit#test.com',
_id: '-1' } ]
[ { devices: {},
address: '123 Fake St, Waterloo, On',
location: 'Unittest',
owner: 'unit#test.com',
_id: '-1' } ]
1) should list the test location
0 passing (2s)
1 failing
1) Testing the functions that deal with users and locations: function locationList should list the test location:
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
at null.<anonymous> (C:\Users\My Name\AppData\Roaming\npm\node_modules\mocha\lib\runnable.js:189:19)
Extending the timeout doesn't work. Nor does putting something random (ie. the integer 1 in the .should.be.deep.equal() function.
My guess is that the callback in exports.locationList is called synchronously, and that your test case is actually failing, throwing an exception, which never gets caught because the callback is (synchronously) called from within a promise handler (more info here).
Try and see if this works better:
dbFunctions.locationList(req, function(result) {
setImmediate(function() {
console.log(result) //Prints the whole result
console.log(testData.locationList)
result.should.exist; //This doesn't cause it to hang
result.should.be.deep.equal(testData.locationList) //hangs here
done(result);
});
});
// or use a module like dezalgo (https://github.com/npm/dezalgo)
The underlying cause may be mockgoose.
Also, you're not using the proper Node.js conventions where the first argument to a callback function is "reserved" for errors. In other words, your code should look similar to this:
if (err) {
callback(err);
} else {
callback(null, data);
}
You're now passing both errors and data as the first argument.
A guess: Maybe the issue lies with mongoose decorating the result with it's own functions/members, and one of them somehow getting stuck in an infinite loop when chai tries to enumerate them all to perform a deep comparison.
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');
});
};
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'
});
});
}
I have a simple Mongoose schema that I'm testing with Mocha; when I run the test with 'success' callback it executes normally, however when the last test executes it fails and appears to run the test again (I get two conclusions, one which populates the error object and the second one which returns null in the error object.) Running the two tests below results in the following output:
Cabinet:
â should return all authorized
â should return not authorized <-- it succeeds the first time?
1) should return not authorized
2 passing (42ms)
1 failing <--- what? there are only two tests
1) Cabinet: should return not authorized :
Uncaught AssertionError: expected null to exist <--- see test
THIS TEST REPEATS
it("should return not authorized error ", function(done){
var newCabinet = {
name: "A new cabinet",
description: "Some remote cabinet",
authorizedBorrowers : ["imatest","imanothertest"],
cabinetInventory : []
};
Cabinet.newCabinet(newCabinet, function(err, cabinet){
if (err) {
console.log("Unable to create cabinet");
done(err);
}
Cabinet.isAuthorizedBorrower(cabinet._id, "bob", function(cberr, borrower){
should.exist(cberr); <-- 'expected null to exist' points here
done();
});
});
});
THIS TEST WORKS
it("should not return unauthorized error ", function(done){
var newCabinet = {
name: "A new cabinet",
description: "Some remote cabinet",
authorizedBorrowers : ["imatest","imanothertest"],
cabinetInventory : []
};
Cabinet.newCabinet(newCabinet, function(err, cabinet){
if (err) {
console.log("Unable to create cabinet");
done(err);
}
//console.log("ID " + cabinet._id)
Cabinet.isAuthorizedBorrower(cabinet._id, "imatest", function(cberr, borrower){
should.not.exist(cberr);
done();
});
});
});
The schema
var cabinetSchema = new Schema({
name: String,
description: String,
thumbnail : Buffer,
authorizedBorrowers : [],
defaultCheckout : {type : Number, default: 0} // default checkout mins = no time
});
var hasBorrower = function(cabinet, borrower){
if (cabinet===null) return false;
if (cabinet.authorizedBorrowers.length === 0) return false;
return (cabinet.authorizedBorrowers.indexOf(borrower) >= 0)
}
cabinetSchema.statics.isAuthorizedBorrower = function(cabinet_id, borrowername, cb ){
this.findOne({_id: cabinet_id}, function(err, cabinet){
if (err) cb(err,null);
if (!hasBorrower(cabinet, borrowername)) cb(new Error(errorMsgs.borrowerNotAuthorized),null);
cb(null,borrowername);
});
};
Whenever you do this, add a return; to avoid calling the done callback twice. This is for mocha but also for general node.js callback handling.
if (err) {
console.log("Unable to create cabinet");
done(err);
return;
}
Same problem in your Cabinet schema:
if (err) cb(err,null);
That needs a return or it will invoke the callback twice and cause chaos (also affectionately known amongst the node.js blogosphere as one flavor of "releasing Zalgo").