I was trying to solve an issue with starting the ExpressJS server before each single test and closing it after each test completes, just to make sure each test is running under the same conditions.
Here is the error I ran into while trying to run the tests
$ ./node_modules/jasmine/bin/jasmine.js
Started
started
events.js:160
throw er; // Unhandled 'error' event
^
Error: listen EADDRINUSE :::3000
Jasmine fails on second test because my code didn't handle start/close properly on each test.
- jasmine - https://github.com/mhevery/jasmine-node
- request - https://github.com/request/request
$ npm install jasmine request --save-dev
.
├── routes
│ ├── city.js
│ └── people.js
├── server.js
└── spec
├── citySpec.js
├── globalSpec.js
├── peopleSpec.js
server.js
function run(callback) {
const express = require('express');
const bodyParser = require('body-parser');
const city = require('./routes/city');
const people = require('./routes/people');
const app = express();
app.use(bodyParser.json());
app.use(city);
app.use(people);
var server = app.listen(3000, function () {
console.log('started');
if (callback) {
callback();
}
});
server.on('close', function () {
console.log('closed');
});
return server;
}
if (require.main === module) {
run();
}
exports.run = run;
server.js
exposes run
function which is responsible for running the server and returning the node's http.Server object (also, see app.listen).
run
function accepts an optional callback
function, it will be called after the server started running, later, we will see how this helps us.
Wrapping all code inside function allows us to create server on demand but it breaks our ability to run it directly via command line because run
is not called inside server.js
, therefore, that's why there is an if statement at the end that checks if run
should be called whenserver.js
runs directly, and not to be called when required as module, see require.main.
routes/city.js
const express = require('express');
const router = express.Router();
router.get('/city', function(request, response) {
response.status(200).json({'city': 'Tel-Aviv'});
});
module.exports = router;
routes/people.js
const express = require('express');
const router = express.Router();
router.get('/people', function(request, response) {
response.status(200).json([{'name': 'Cate Blanchett'}]);
});
module.exports = router;
spec/citySpec.js
const request = require('request');
const server = require('../server');
const endpoint = 'http://localhost:3000/city';
describe('city', function () {
it('should return 200 response code', function (done) {
request.get(endpoint, function (error, response) {
expect(response.statusCode).toEqual(200);
done();
});
});
it('should fail on POST', function (done) {
request.post(endpoint, {json: true, body: {}}, function (error, response) {
expect(response.statusCode).toEqual(404);
done();
});
});
});
spec/peopleSpec.js
const request = require('request');
const server = require('../server');
const endpoint = 'http://localhost:3000/people';
describe('people', function () {
it('should return 200 response code', function (done) {
request.get(endpoint, function (error, response) {
expect(response.statusCode).toEqual(200);
done();
});
});
it('should fail on POST', function (done) {
request.post(endpoint, {json: true, body: {}}, function (error, response) {
expect(response.statusCode).toEqual(404);
done();
});
});
});
spec/globalSpec.js
const server = require('../server');
var serverInstance;
beforeEach(function (done) {
serverInstance = server.run(done);
});
afterEach(function (done) {
serverInstance.close(done);
});
Before each test we will create a new server instance and pass jasmine's done
function to run
, so when server starts running and ready to accept requests it will call done
, lastly, when that happens, only then beforeEach()
will complete running and next test will start.
After each test we will close the server by calling close
method and passing done
as callback.
$ ./node_modules/jasmine/bin/jasmine.js
Started
started
closed
.started
closed
.started
closed
.started
closed
.
4 specs, 0 failures
Finished in 0.138 seconds