I'm using mocha and supertest to test a REST service. In this case, I need to test that a subsequent login after a GET request returns the query from that GET request.
Since the above test is going to be performed several times for various endpoints, this is what I have come up with:
/*
* getRequest.js
*/
'use strict';
var request = require('supertest')
, verifyUrl = require('./verifyUrl') // verifies basic url formatting
;
module.exports = getRequest;
/**
* Perform GET request using given parameters and call next when finished.
* @param getEnvironment {json}
* @param getEnvironment.url {string}
* @param getEnvironment.endPoint {string}
* @param getEnvironment.authorization {string}
* @param next {function} The callback
*/
function(getEnvironment, getParams, next) {
var url = verifyUrl(getEnvironment.url);
request(url)
.get(getEnvironment.endPoint)
.set("authorization", getEnvironment.authorization)
.set('Accept', 'application/json')
.query(getParams)
.end(next)
}
/*
* loginWithCallback.js
*
* Performs a basic login but depends on the callback to test the results of the login.
*/
'use strict';
var request = require('supertest')
, VerifyUrl = require('./VerifyUrl')
;
module.exports = LoginWithCallback;
/**
* Perform basic login, then call next passing in err and res for testing.
*
* @param setEnvironment {json} Expects url and authorization. Any other parameters will be ignored.
* @param setEnvironment.url {string} The url being tested.
* @param setEnvironment.authorization {string}
* @param next {function} callback function that will perform the actual test.
*/
function LoginWithCallback(setEnvironment, next) {
var url = VerifyUrl(setEnvironment.url);
request(url)
.post('api/users/login')
.set('authorization', setEnvironment.authorization)
.set('Accept', 'application/json')
.end(next);
}
/*
* e2eTest.js
*/
'use strict';
var base64_encode = require('Base64').btoa
, should = require('should')
, jsonValidator = require('is-my-json-valid')
, mergeJSON = require('lodash/merge')
, lodashClone = require('lodash/clone')
, responseSchema = require('./responseSchemas/200.json')
, login = require('./loginWithCallback')
, getRequest = require('./getRequest')
;
var username = "newUser" + Date.now()
, password = "passW0rd"
, testEnvironment = {
"url": "http://localhost:9000/",
"endPoint": "api/users/login",
"authorization": "Basic " + base64_encode(username + ":" + password)
}
;
var expectedResult = {};
describe('End to End testing on' + JSON.stringify(testEnvironment), function () {
describe('new user, ' + username + ', login', function () {
it('should return 200 and an empty body message.', function (done) {
login(testEnvironment, function (err, res) {
if (err) {
done(err);
}
res.statusCode.should.deepEqual(200);
var jsonValidate = jsonValidator(responseSchema, {verbose: true});
jsonValidate(res.body).should.be.true("Response body failed schema check: \n" +
JSON.stringify(jsonValidate.errors, null, 4));
res.body.should.deepEqual(expectedResult);
done();
});
});
});
describe('user, ' + username + ', logs in after performing get request', function () {
var getRequestParams = {"firstName":"john", "lastName":"doe"};
beforeEach(function() {
var getEnviron = lodashClone(testEnvironment);
getEnviron.endPoint = 'api/persons/findName';
mergeJSON(expectedResult, {"persons": {"findName": getRequestParams}});
getRequest(getEnviron, getReqestParams, function(err, res) {
if (err) {
done(err);
}
console.log("GET request: " JSON.stringify(res, null, 2));
done();
});
});
it('should return 200 and query values', function(done) {
login(testEnvironment, function (err, res) {
if (err) {
done(err)
}
console.log("it test: " + JSON.stringify(res, null, 2));
res.statusCode.should.deepEqual(200);
var jsonValidate = jsonValidator(responseSchema, {verbose: true});
jsonValidate(res.body).should.be.true("Response body failed schema check: \n" + JSON.stringify(jsonValidate.errors, null, 4));
res.body.should.deepEqual(expectedResult);
done();
});
});
});
What the above three files are supposed to do is the following: 1. create a new user 2. login new user and test its response for no previous query params. (passes) 3. perform the GET request and then print the result in the before block 4. perform the login of the user print and test results in the it block.
But what I'm getting is something like:
End to End testing on{"url":"http://localhost:9000/","endPoint":"api/users/login","authorization":"Basic bmV3VXNlcjE0NjY0NDI0OTEzNDc6cGFzc1cwcmQ="}
new user, newUser1466442491347, login
✓ should return 200 and an empty body message. (54ms)
user, newUser1466442491347, logs in after performing persons/findByName request
it test: [res text]
1) should return and query values
GET request: [res text]
1 passing
1 failing
Uncaught AssertionError: expected Object {} to equal Object {
persons: Object { findByName: Object { firstName: 'joe', lastName: 'jones' } }
}
As can be seen, the 'it' block is appearing to run before the before block has finished. From my reading of mocha, this should not happen as it waits for the before and beforeEach finishes before running the 'it' block. But, perhaps, the callback for the getRequest gets queued after the it block?
What am I doing wrong or misunderstanding?
You definitely must set your beforeEach
hook so that Mocha knows it is asynchronous: either you return a promise to Mocha or you use the done
callback. In order to use the done
callback you must declare a callback that takes done
:
beforeEach(function (done) {
(You could call it anything you want (e.g. finished
), so long as you are consistent (call finished()
later).)