7/02/2013

Using Nock and Jasmine-Node to Target Your Server's HTTP Requests

I am continuing along my path of "test driven development" discovery and am finding it fraught with challenges. All my progress has shown me how crucial TDD is for success, and I am constantly reminded of how effective it is. The latest challenge I had was to test the outgoing http call of my node server. I was testing a module that sent an http request in a private method. It was crucial that my testing confirm the outgoing URL. This is not as straight forward as it sounds.

I didn't want to expose the object's private functionality so I didn't want to add any getters that would allow access to private resources. Jasmine comes with a very cool mechanism for tracking and interfering with the methods of an object. However, I didn't want to interfere with the method. I needed to let the method run so I could see the url and data being passed into the get request.

Unit testing is based on the idea that a specific value passed to an operation will have the predicted result. One problem with testing a server-side http call is having something to respond to it. I needed the request to be issued but it had to go nowhere, and had to return introspective data I could expect. I searched but couldn't find many options that worked well for my situation. Finally, as the red lenses of frustration began forming over my eyes, I found Nock.

Nock is a small library that can fit into most existing testing frameworks. It describes itself as an "HTTP mocking and expectations library". This library is an easy way to interrupt http calls and return a response(s). Nock attaches itself to the server's native http module. It intercepts requests going through http and compares them to URLs it is looking for. If it finds a URL it is looking for, the request is stopped and sent back with the reply detailed in the setup.

I was building tests against a simple class whose purpose was to request data from a secondary web service and return the result. I created a simple illustration for this example:

var http = require ('http');
exports = module.exports = function (fn) {
http.get ('http://fakeserver/dayreader', function (res) {
res.setEncoding ('utf8');
res.on ('data', function (data){
fn.call (this, data);
});
})
}

The module exports a function. Initially, it makes an http request to the url "http://fakeserver/dayreader". The server runs a callback function, provided by the caller, when data is returned. This is a simple idea but it isn't so easy to test. A spy, like the Jasmine spy functionality, could interrupt the method and return a fake result. That would not help here. I needed to examine the outgoing get request directly. I wanted to confirm that parameters were appended to the get url correctly. I wanted to test to see if the module handled error status codes. The tests also checked that the response data was correct despite manipulation by the module object. Those are just a few of the ways a function like this could be tested.

Let's take a look at the jasmine test:

var myDay = require ('./../MyDay.js'),
nock = require ('nock');

describe ('How was my day response', function () {
it ('test a good day', function (done) {
nock ('http://fakeserver')
.get ('/dayreader')
.reply (200, {day:'good'});

new myDay (function (data){
data = JSON.parse(data);
expect (data.day).toEqual ('good');
done ();
})
})
})

Nock is the very first thing in the test, and has a chain of operations.

nock ('http://fakeserver')

The initial call to nock sets up a server-wide listener for calls to "http://fakserver". Any calls to that host are examined by nock first. Requests to urls not being managed by Nock are made normally. Nock finds the URL it is searching for and fires. By default, the listener will be removed after the first time it is fired. The Nock listener only lasts until it fires to avoid interfering with future tests. There are options you can set to tell Nock to persist the listener if needed.

.get ('/dayreader')

The second operation in the Nock chain identifies the specific http path and verb to interrupt. In the example above, Nock will stop the next get request to "http://fakeserver/dayreader".

The third line in the chain tells Nock what result to return to the caller.

.reply (200, {day:'good'});

The first parameter is a status code. In the example, we are returning 200 to represent a successful result. This number can be anything and can be used to test against a whole range of responses. The second parameter can be a string, a literal object, or pulled from a file. My favorite return value for the second parameter is a function. A function can be sent in to run when the response is being sent. The function accepts two parameters: the url of the request, and the body of the request.

The rest of the test is built as a standard Jasmine-Node asynchronous operation. A callback function is passed to the myDay function. The function that Jasmine-Node needs to complete the test (the done function) is available to the callback function thanks to the magic of closures. The result is a successful test. The above code is attached to this post and included in a zip file for reference.

This small post barely touches the many ways that Nock and Jasmine-Node can be used together. Nock provides a way to test your interactions with remote resources from the safety of your own Jasmine-Node sandbox.

Download: Example Files
References:
Jasmine-Node
Nock