If you have worked with Express.js to build an API or application you will likely be familiar with the following boilerplate:
# index.js
var express = require('express');
var app = express();
app.get('/hello-world', function (req, res) {
res.send('Hello World!');
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
Essentially, you instantiate the app, add a route to it, and have it “listen” for requests. Above, we are doing this all in one file. When I first started with Express, my app.js or index.js files often looked like this (though the route definitions were in a separate file). It works. However, when you want to “unit” or “integration” test your routes (meaning you actual hit the routes with test requests), it gets a little less simple. With the code above, you would actually have to start the app and get it listening on port 3000. Then your test-requests would presumably have to hit 127.0.0.1:3000/hello-world in order to get a response. Sounds okay, but leads to a few complications. You’ve got to litter your tests with references to 127.0.0.1:3000 or at least parameterize that fragment of the route-string. Also, you will have to make sure the app is “listening” prior to test execution – either manually or with some kind of “before” function that runs exactly once before all tests.
I guess this could work, but there is a better way, because you DON’T need to get your ExpressJS app “listening” to get requests to a given route. Instead, you leverage a NPM called supertest to target your app’s routes. However, you will need to setup your Express app differently than noted above:
# index.js
var express = require('express');
var app = express();
app.get('/hello-world', function (req, res) {
res.send('Hello World!');
});
module.exports = app;
Notice we have removed the app.listen() and exported app via module.exports. Now create a second file called server.js that requires index.js.
# server.js
var app = require('./index.js');
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
When you want to start the app, you do it with server.js:
$ node server.js
Example app listening on port 3000!
BUT…when you want to test, you use index.js directly. Here’s an example test spec file (I use the mocha framework).
# testSpec.js
var supertest = require('supertest');
var app = require('index.js');
describe('GET /hello-world', function () {
it('should have status 200 and contain specified data structure', function (done) {
supertest(app)
.get('/hello-world')
.end(function (err, res) {
// Test that the endpoint exists and responds
expect(res).that.equals('Hello World!');
done();
});
});
});
Notice we are able to access the application via require(‘index.js’) and then pass it to supertest. supertest then handles the specifics of the request/response chain, and allows you to focus on response assertions.