How To Test Your Express Controllers

by Stefan Fidanov

You have already seen how to test your models at
Testing Your Mongo Models for Node & Express,
but your Express controllers are also an integral part of your web application. When they
don’t work, your users see 404 and 500 errors, and they might never come back.

This is why your controllers deserve to be tested. No matter what your users throw
at them, they should be able to handle it. They are the gate to the internet for
your app and people will throw anything at them. You better prepare for it.

However, unlike other parts of your app, your controllers are not independent units.
They often depend on a lot of models and exterior services.

This makes them more tricky to test, but there are still ways to do it.

Which are the dependencies of a controller?

The main dependencies of your controllers are the user requests, your models
and exterior services.

The user requests may not seem like a dependency but they are, because they are not
generated by you but by some exterior force :-)

Your controllers use the models to get data from the database and to apply some
business logic. As a result you depend not only on your models but also on your
database.

Your exterior services are services which you are connecting to over HTTP. You
can own them or they can be unrelated to you like Twitter or Facebook.

How do you test a controller?

As any other unit of your application, your initial goal is to test it independently.
However, you saw that each controller can have some dependencies.

The solution to this problem is to mock your dependcies. You have to find a way
to control them.

Initial Setup

However, before everything there are two module that I like to use no matter
what I am testing, and I am going to use them today, too.

The only dependencies of this controller are the requests and response objects
required by each handler. These are the items that you should mock first when
testing your controllers.

node-mocks-http

Let’s welcome node-mocks-http module which will help you with creating request
and response objects under for testing your controller.

$ npm install node-mocks-http --save-dev

node-mocks-http can create request and response objects which you can then
pass on to your controller for handling. Then you can see what was the result and
check if it was what you expected.

Mocking your requests and responses

Here are the tests for the controller that you just saw.

controller=require('../../controllers/welcome'),http_mocks=require('node-mocks-http'),should=require('should')functionbuildResponse(){returnhttp_mocks.createResponse({eventEmitter:require('events').EventEmitter})}describe('Welcome Controller Tests',function(){it('hello',function(done){varresponse=buildResponse()varrequest=http_mocks.createRequest({method:'GET',url:'/hello',})response.on('end',function(){response._getData().should.equal('world');done()})controller.handle(request,response)})it('hello fail',function(done){varresponse=buildResponse()varrequest=http_mocks.createRequest({method:'POST',url:'/hello',})response.on('end',function(){// POST method should not exist.
// This part of the code should never execute.
done(newError("Received a response"))})controller.handle(request,response,function(){done()})})it('upper',function(done){varresponse=buildResponse()varrequest=http_mocks.createRequest({method:'GET',url:'/upper/monkeys',})response.on('end',function(){response._getData().should.equal('MONKEYS');done()})controller.handle(request,response)})})

Let’s have a closer look at the tests from above.

There is a helper method buildResponse which uses the mock library to build a
response object with some default settings. In this case it adds an event emitter.

It is a good practice to always have this event emitter. The only way to know
that a controller has finished its work and rendered a response is when it emits
the end event. This is very useful when there are asynchronous requests to a
database or a web service.

In each test we also create a request object, by providing a url and a method.

Then we wait for the end event to know that the controller has processed the
request. This is where you have to call the done function to let know mocha
that you have finished testing this piece of code.

Finally, you call the handle method of the controller to dispatch your request,
based on the url and the method.

However, the end event is sent only when a proper combination url and method
is found. Otherwise, the third and optional argument of the handle method is called.

This is the next function, just like in a middleware because controllers are
essentially middlewares.

You can see that we use this in the second test to check that the controller does
not handle POST request for the /hello url.

Mocking Models

However, most controllers are a little bit more complex than the one from our first
example. They usually at least talk with a model.

Let’s have a look at the following controller.

varexpress=require('express'),router=express.Router(),model=require('../models/news')router.get('/all',function(req,res){model.all(function(err,items){if(err)returnres.json({error:"There is a problem"})res.json({error:null,news:items})})})router.post('/create',function(req,res){model.create(req.body.title,req.body.text,function(err,doc){if(err)returnres.json({error:"There is a problem"})res.json({error:null,news:doc})})})module.exports=router

In this controller, you are reading from and writing to a database while using
a model.

mockery

This is an external dependencies that we would like to control. That is why we
are going to use mockery

npminstallmockery--save-dev

mockery killer feature is that when a module is loaded with require it can
provide a different object instead of the one returned by the module.

This is all done transparently to your code.

To make this work you need to first call mockery.enable() before loading the
module and then mockery.disable() when you are done.

As you can see, you need to load the controller in the before method which executes
exactly once, but not before you enable the mockery module with mockery.enable().

Just after enabling it you have to load the mock object, in this case the mock model.

In the first test, all we do is reading data from the database. However, thanks
to the mocking object you have full control on what is returned and you can
verify whether your controller returns it correctly.

In the second test, we test what happens when we send some data to a post controller
which is supposed to save it. The mock object helps us to check whether the controller
tried to save the data.

As you can see mocking models is very easy with mockery. Actually mocking any
kind of module is very easy, like an SDK for a framework for example.

Mocking Services

There is one last dependency that you might need to handle. Your app can depend
on exterior web services.

Maybe it loads data from Facebook or from some other place. It is important to
mock them not only because you need to control what they return during tests, but
because real HTTP requests are slow compared to how fast your tests usually run.

nock

Thankfully there is a module named nock which can help with that

$ npm install nock --save-dev

Whenever an http request is made nock intercepts it and responds with whatever
you want. You can select the data it sends back, the status code and much more.

Did you like this article?

Please share it

Enter your email and get our NPM Cheat Sheet for NodeJS Developers and
the links to our 5 most popular articles which have helped thousands of
developers build faster, more reliable and easier to maintain Node applications.