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.
mocha provides a very convenient way to write and run tests.
should gives you a ton of nice methods and properties to verify that the results are what you expect.
You will learn more about them from the tests below. Anytime you need to run the tests in your tests folder, all you should do is run
Mocking Requests
Let’s have a look at the following simple Express controller.
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.
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.
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.
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
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.
Let’s have a look at how it works in the tests of our controller.
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
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.
Let’s have a look at what your controller might look like.
In this simple controller all we do is using the request module to easily load some data from Google’s home page and then return it back.
Let’s have a look how to handle that situation in your tests
It is very simple to use nock. You just have to tell it which url it should intercept and what it should return.
In this case it always responds with status code 200 (success) and with ‘something funny’. Even when the network is down you are still going to be able to run your tests.
The only thing you should be careful about is that each time you setup nock to handle the request for a specific path, it will handle it just once, and then allow the request next time.
What if I have some other dependencies?
You can probably use the tools that we used above to mock how this module works.
If it is something that you use require to load you can use mockery. If is something over a network, then you would probably use nock.
Next
You have learned how to test your controllers. You can begin applying this knowledge immediately with your simplest controllers and continue with the rest of them.
You can explore what other features mockery, nock and node-mocks-http provide. They have much more to offer than what I just showed you.
Other articles that you may like