How to force a function to execute first in Jasmine test? - javascript

I have a route in Node that gets an auth key. I want to use this auth key in all my jasmine tests as a parameter in the URL request. I want SetUp function to run, set a global var, then allow me to use this variable in all the rest of the test cases.
SetUp Function
var global_key = request({
uri : 'http://localhost:3000/grabToken',
method : 'GET'
},
function (err, body, res) {
if (err) { console.log(err);}
else {
return body['auth_key'];
}
});
Test Suite
function testCases() {
describe(TEST_SUITE, function() {
describe("GET /retrieveSecret/VALID_UUID", function() {
it('Requests the secret - Successful response', function(done) {
// ...
}
}
}
}

You could use asynchronous version of beforeAll function:
describe(TEST_SUITE, function() {
let key;
beforeAll(function (done) {
const params = { uri: 'http://localhost:3000/grabToken', method: 'GET' };
request(params, function (err, body, res) {
if (err) {
console.log(err);
done.fail();
} else {
key = body['auth_key'];
done();
}
});
})
describe("GET /retrieveSecret/VALID_UUID", function() {
it('Requests the secret - Successful response', function(done) {
// `key` is available here
}
});
})

Related

How to test callback of function in Chai?

I am using Slack API and I want to test does it work fine with response status code. Here is sending function :
sendMsg(msg) {
return this.slack.webhook({text: msg}, (err, res) => {
if (err) {
throw err;
}
console.log(res.statusCode) // = 200
return res.statusCode;
});
}
And my test:
it('Checks connection with Slack', (() => {
let slack = new Slack();
let res = slack.sendMsg('test');
expect(res).to.equal(200);
}));
But ofc. it's giving me request object to slack. I want to wait for response object from slack API. Thanks in advance.
It looks like slack.webhook takes in a callback, which is how you retrieve the status. The problem is that the caller of sendMsg has no way of getting that status.
One way to solve this is to have sendMsg take in a callback:
sendMsg(msg, onStatusReceived) {
this.slack.webhook({text: msg}, (err, res) => {
if (err) {
throw err;
}
console.log(res.statusCode) // = 200
onStatusReceived(res.statusCode);
});
}
Then in your test, use done to end the test when the callback is invoked:
it('Checks connection with Slack', (done) => {
let slack = new Slack();
slack.sendMsg('message', status => {
expect(status).to.equal(200);
done();
});
});
Another way is to have sendMsg wrap slack.webhook in a promise, so the caller can do sendMsg().then(...).
one of the ways I handled a returning callback to test is as follows:
it('receives successful response', async () => {
nock('https://localhost')
.persist()
.log(console.log)
.post(‘/getData’, (unitData, callback) => {
return true;
})
.delayBody(1000)
.reply(200, {statusCode: 'Some Status'});
const getSomeData = await getResponse(unitData, function callBack(unitData, error, data){
expect(data.statusCode).to.be.equal(200);
}) })
getResponse Function (returning callback):
getResponse(unitData, function callBack(unitData, error, data){
try {
return request.post(unitData, function (err, resp) {
if (!err && resp.statusCode === 200) {
if (resp.body.error) {
return callback(obj, JSON.stringify(resp.body.error), null);
}
return callback(obj, null, resp);
} else {
if (err == null) {
err = { statusCode: resp.statusCode, error: 'Error occured.' };
}
return callback(obj, err, null);
}
});
} catch (err) {
return callback(obj, err, null);
}
}

JWT test case mocking in nodejs

I am trying to write a test case for jwt token validation in node js.I am able to cover failure cases but not successful case.
isValid: function (request, reply) {
if (request.query && request.query.token) {
var token = request.query.token;
validateTok(token)
.then(function (credentials) {
reply(true);
})
.catch(function (err) {
reply(false);
})
} else {
reply(false);
}
}
function validateT(jwt) {
return new Promise(function (resolve, reject) {
Security.validate(jwt, function (err, success, credentials) {
if (err || !success) {
reject(err)
} else {
resolve(credentials);
}
});
});
};
I want to mock Security.validate(jwt, function (err, success, credentials) to return success. Following is my test case.
it('should pass token validation', async () => {
const data = {
token: '1512598739676174ae69792b81583fd210c381c50f',
};
const request = generateRequest({query: data,});
const response = await awaitHandler(users.isValid, request);
(response).should.eql( true );
});

Test and stub params in POST request

Guys how can I stub params in POST request, for example here a part of function
gridCalculator : function(req,res){
// calculation logic
var options=[];
options.dateFirstLicensed = req.param('DateFirstLicensed');
options.dateFirstInsured = req.param('DateFirstInsured');
options.claimList = req.param('ClaimList');
options.suspenList = req.param('SuspenList');
...etc
if I did this
it('grid Calculate', function (done) {
var req = {
'DateFirstLicensed' : "01-01-2010",
'DateFirstInsured': "01-01-2011",
'ClaimList': ['05-03-2012'],
'SuspenList': [{'StartDate':'05-03-2012','EndDate':'05-05-2012' }]
};
gridCalculator.gridCalculator(req,function (err, result) {
result.should.exist;
done();
});
});
I get error because I'm simply passing an object not POST request
TypeError: req.param is not a function
Two options come to mind (there are probably a lot more):
Option 1: Define the param function yourself:
it('grid Calculate', function (done) {
var params = function(param) {
switch (param) {
case 'DateFirstLicensed':
return "01-01-2010";
case 'DateFirstInsured':
... //do the same for all params
}
};
var req = {
param: params
};
gridCalculator.gridCalculator(req,function (err, result) {
result.should.exist;
done();
});
});
Option 2: Use tools like supertest to create calls to your server's endpoint.
The problem is that you don't stub the function that is used in your gridCalculator method in your test.
It should look like this:
it('grid Calculate', function (done) {
var testParams = {
'DateFirstLicensed' : "01-01-2010",
'DateFirstInsured': "01-01-2011",
'ClaimList': ['05-03-2012'],
'SuspenList': [{'StartDate':'05-03-2012','EndDate':'05-05-2012'}]
};
var req = {
param: function (paramName) {
return testParams[paramName];
}
};
gridCalculator.gridCalculator(req,function (err, result) {
result.should.exist;
done();
});
});

How can I use node async to fetch my mongoose calls?

I'm building a site with node/express/mongoose and it needs to do the following things when viewing a submission.
The problem I'm running into is doing db fetches in a non-serial fashion. For example, I'll do a few calls to fetch some data, but some of the calls might not finish until the execution context goes to the other. Tried to use the npm module, async, but am having trouble trying to figure out how I would integrate it.
Here is my code:
var getViewCount = function(submissionId) {
Submission.getSubmissionViewCount({
submissionId : submissionId
}, function(err, count) {
if (err) {
throw err;
}
if (count) {
return count;
}
});
};
var getVotes = function(submissionId) {
console.log('getvotes');
Submission.getSubmissionVotes({
submissionId : submissionId
}, function(err, votes) {
return votes;
});
};
var getSubmission = function(id) {
Submission.getSubmission({
id : id
}, function(err, submission) {
if (err) {
throw err;
}
if (submission) {
return submission;
}
});
};
var renderSubmission = function(title, submission, views) {
res.render('submission', {
title: submission.title + ' -',
submission: submission,
views: views.length
});
};
How do I use this with async? Or should I be using async.series isntead of async.async?
async.series([
function(callback) {
var submission = getSubmission(id);
callback(null, submission);
},
function(callback) {
// getViewCount(submissionId);
},
function(callback) {
// getVotes(submissionId);
},
function(callback) {
//renderSubmission(title, submission, views);
}
], function(err, results) {
console.log(results);
});
Basically I want to fetch the views and votes first and then render my submission.
TheBrain's description of the overall structural changes that you should make to your code is accurate. The basic methodology in Node is to nest a series of callbacks; very rarely should you require functions that actually return a value. Instead, you define a function that takes a callback as parameter and pass the result into that callback. Please review the code below for clarification (where cb is a callback function):
var getViewCount = function(submissionId, cb) {
Submission.getSubmissionViewCount({
submissionId : submissionId
}, function(err, count) {
if (err) {
throw err;
}
if (cb) {
cb(count);
}
});
};
var getVotes = function(submissionId, cb) {
console.log('getvotes');
Submission.getSubmissionVotes({
submissionId : submissionId
}, function(err, votes) {
if (cb) {
cb(votes);
}
});
};
var getSubmission = function(id, cb) {
Submission.getSubmission({
id : id
}, function(err, submission) {
if (err) {
throw err;
}
if (cb) {
cb(submission);
}
});
};
var renderSubmission = function(submissionId) {
getSubmission(submissionId, function (submission) {
if (!submission) {
// unable to find submission
// add proper error handling here
} else {
getViewCount(submissionId, function (viewCount) {
res.render('submission', {
title: submission.title + ' -',
submission: submission,
views: viewCount
});
});
}
};
};

Testing asynchronous function with mocha

I want to test a asynchronous javascript function that runs in node.js and makes a simple request to a http api:
const HOST = 'localhost';
const PORT = 80;
http = require('http');
var options = {
host: HOST,
port: PORT,
path: '/api/getUser/?userCookieId=26cf7a34c0b91335fbb701f35d118c4c32566bce',
method: 'GET'
};
doRequest(options, myCallback);
function doRequest(options, callback) {
var protocol = options.port == 443 ? https : http;
var req = protocol.request(options, function(res) {
var output = '';
res.setEncoding('utf8');
res.on('data', function(chunk) {
console.log(chunk);
output += chunk;
});
res.on('error', function(err) {
throw err;
});
res.on('end', function() {
var dataRes = JSON.parse(output);
if(res.statusCode != 200) {
throw new Error('error: ' + res.statusCode);
} else {
try {
callback(dataRes);
} catch(err) {
throw err;
}
}
});
});
req.on('error', function(err) {
throw err;
});
req.end();
}
function myCallback(dataRes) {
console.log(dataRes);
}
Executed this code works and the response will be displayed as expected.
If I execute this in a mocha test the request is not executed:
describe('api', function() {
it('should load a user', function() {
assert.doesNotThrow(function() {
doRequest(options, myCallback, function(err) {
if (err) throw err;
done();
});
});
assert.equal(res, '{Object ... }');
});
});
The Problem is, that no code after:
var req = protocol.request(options, function(res) {
is executed not even a simple console.log.
Can anybody help?
You have to specify the callback done as the argument to the function which is provided to mocha - in this case the it() function. Like so:
describe('api', function() {
it('should load a user', function(done) { // added "done" as parameter
assert.doesNotThrow(function() {
doRequest(options, function(res) {
assert.equal(res, '{Object ... }'); // will not fail assert.doesNotThrow
done(); // call "done()" the parameter
}, function(err) {
if (err) throw err; // will fail the assert.doesNotThrow
done(); // call "done()" the parameter
});
});
});
});
Also, the signature of doRequest(options, callback) specifies two arguments though when you call it in the test you provide three.
Mocha probably couldn't find the method doRequest(arg1,arg2,arg3).
Did it not provide some error output? Maybe you can change the mocha options to get more information.
EDIT :
andho is right, the second assert would be called in parallel to assert.doesNotThrow while it should only be called in the success callback.
I have fixed the example code.
EDIT 2:
Or, to simplify the error handling (see Dan M.'s comment):
describe('api', function() {
it('should load a user', function(done) { // added "done" as parameter
assert.doesNotThrow(function() {
doRequest(options, function(res) {
assert.equal(res, '{Object ... }'); // will not fail assert.doesNotThrow
done(); // call "done()" the parameter
}, done);
});
});
});
If you have an asynchronous function that does not support callbacks, or if you think using unnecessary callbacks is... unnecessary, then you can also just turn the test into an async test.
instead of:
it('should be able to do something', function () {});
simply do:
it('should be able to do something', async function () {});
^^^^^
Now you can await async functions:
it('should be able to do something', async function () {
this.timeout(40000);
var result = await someComplexFunction();
assert.isBelow(result, 3);
});
I've done a very similar test in my project for an http client. I paste the code here and hope is useful.
Here is the client (my nodejs server use express and I use promise for error handling):
var http = require('http');
var querystring = require('querystring');
module.exports = {
get: function(action, params, res, callback) {
doPromiseRequest(action, querystring.stringify(params), callback, 'GET', 'application/json')
.then((response) => callback(response))
.catch((error) => {
res.status(500);
res.render('error', {layout: false, message: error.message, code: 500});
});
},
}
function doPromiseRequest(action, params, callback, method, contentType) {
var options = {
hostname: 'localhost',
port: 3000,
path: '/api/v1/' + action.toString(),
method: method,
headers: {
'Content-Type': contentType,
'Content-Length': Buffer.byteLength(params)
}
};
return new Promise( (resolve, reject) => {
var req = http.request(options,
function(response) {
response.setEncoding('utf8');
var data = '';
response.on('data', function(chunk) {
data += chunk;
});
response.on('end', function() {
var parsedResponse;
try {
parsedResponse = JSON.parse(data);
} catch(err) {
reject({message: `Invalid response from hurricane for ${action}`});
return;
}
if (parsedResponse.error)
reject(parsedResponse.error);
else
resolve(parsedResponse);
});
response.on('error', function(err){
console.log(err.message);
reject(err);
});
});
req.on('error', function(err) {
console.log(err);
reject({message: err.message});
});
req.write(params);
req.end();
});
}
And here is the test:
var http = require('http');
var expect = require('chai').expect;
var sinon = require('sinon');
var PassThrough = require('stream').PassThrough;
describe('Hurricane Client tests', function() {
before(function() {
this.request = sinon.stub(http, 'request');
});
after(function() {
http.request.restore();
});
it('should convert get result to object', function(done) {
var expected = { hello: 'world' };
var response = new PassThrough();
response.statusCode = 200;
response.headers = {}
response.write(JSON.stringify(expected));
response.end();
var request = new PassThrough();
this.request.callsArgWith(1, response).returns(request);
client.get('any', {}, null, function(result) {
expect(result).to.eql(expected);
done();
});
});
});

Categories