Testing expressjs
routes without spinning up a server.
In most integration and end-to-end routes testing, a live server may be deemed critical to make reasonable test assertions. A live server is not always a good idea, especially in a sandboxed environment such as a CI environment where opening server ports may be restricted, if not outright prohibited. In this article, we explore the combination of mocking HTTP requests/responses to make use of an actual server obsolete.
In this article we will talk about:
- Mocking the Server instance
- Mocking Route's Request/Response objects
- Modularization of routes and revealing server instance
- Auto reload(hot reload) using:
nodemon
,supervisor
orforever
Even though this blog post was designed to offer complementary materials to those who bought my Testing
nodejs
Applications book, the content can help any software developer to tuneup working environment. You use this link to buy the book.
Show me the code
//
var User = require('./models').User;
module.exports = function getProfile(req, res, next){
User.findById(req.params.id, function(error, user){
if(error) return next(error);
return res.status(200).json(user);
});
};
//Router that Authentication Middleware
var router = require('express').Router();
var authenticated = require('./middleware/authenticated');
var getUsers = require('./users/get-user');
router.get('/users/:id', authenticated, getUser);
module.exports = router;
What can possibly go wrong?
When trying to figure out how to approach testing expressjs
routes, the driving force behind falling into the integration testing trap is the need to start a server. the following points may be a challenge:
- Routes should be served at any time while testing
- Testing in a sandboxed environments restricts server to use(open new ports, serving requests, etc)
- Mocking request/response objects to wipe need of a server out of the picture
Testing routes without spinning up a server
The key is mocking request/response objects. A typical REST integration testing shares similarities with the following snippet.
var app = require('express').express(),
request = require('./support/http');
describe('req .route', function(){
it('should serve on route /user/:id/edit', function(done){
app.get('/user/:id/edit', function(req, res){
expect(req.route.path).to.equal('/user/:id/edit');
res.end();
});
request(app)
.get('/user/12/edit')
.expect(200, done);
});
it('should serve get requests', function(done){
app.get('/user/:id/edit', function(req, res){
expect(req.route.method).to.equal('get');
res.end();
});
request(app)
.get('/user/12/edit')
.expect(200, done);
});
});
Example:
example from so and
supertest
.supertest
spins up a server if necessary. In case we don't want to have a server, then an alternativedupertest
can be a reasonable alternative.request = require('./support/http')
is the utility that may use either of those two libraries to provide a request.
Choosing tools
If you haven't already, reading “How to choose the right tools” blog post gives insights on a framework we used to choose the tools we suggest in this blog.
Following our own Choosing the right tools framework, we suggest adopting the following tools, when testing expressjs
routes by mocking out the server:
- There exists well respected such as
jasmine
(jasmine-node
),ava
,jest
in the wild.mocha
can just do fine for example sakes. - There is also code instrumentation tools in the wild.
mocha
integrates well withistanbul
test coverage and reporting library. supertest
,nock
anddupertest
are framework for mocking mocking HTTP, whereasnock
intercepts requests.dupertest
responds better to our demands(not spinning up a server).
Workflow
If you haven't already, read the “How to write test cases developers will love”
# In package.json at "test" - add next line
> "istanbul test mocha -- --color --reporter mocha-lcov-reporter specs"
# OR "nyc test mocha -- --color --reporter mocha-lcov-reporter specs"
# Then run the tests using
$ npm test --coverage
Example: istanbul
generates reports as tests progress
Conclusion
To sum up, it pays off to spend extra time writing some tests. Effective tests can be written before, as well as after writing code. The balance should be at the discretion of the developer.
Testing nodejs
routes are quite intimidating on the first encounter. This article contributed to shifting fear into opportunities.
Removing the server dependency makes it easy to validate the most common use cases at a lower cost. Writing a good meaningful message is pure art. There are additional complimentary materials in the “Testing nodejs
applications” book.
References
- Testing
nodejs
Applications book - A TDD Approach to Building a Todo API Using
nodejs
andmongodb
~ SemaphoreCI Community Tutorials - “How to build and test REST with
nodejs
Express Mocha” ~ The Way of Code