Test Driving a Node HTTP Server with James Shore

Recently I have been watching the Let’s Code Test Driven JavaScript video series by James Shore. The series attempts to show the building of an application in node.js applying rigorous testing to the process.

The videos are nice and short at ~15 minutes each. I have been watching them in groups of about 3-5 at a time and following along Learn To Code The Hardway style. And I am about 15 episodes in so far.

The interesting thing is James is obviously not a node programmer. He states clearly he is new to node. So the early videos are less about how to do the right thing in node, but more about watching a disciplined professional programmer set small goals, explore a foreign environment and discovery. That alone is worth cost of entry.

I find myself rooting for James, looking forward to when he discovers package.json and how it will help him manage his dependencies more explicitly. Wondering if he will discover how to write his own modules and publish his lint runner to NPM. Curious if he even sees value in that. Anxious for him to remove his node_modules folder from his git repository because it sets off my OCD.

At the same time I am learning a lot from him about approaching problems. Early on James sets a goal that he wants to write a unit test that validates his server responds to get requests. He proceeds to fail many times at this task, fighting with the intricacies of node development, asynchronous programming and unfamiliar tooling. At each failure, he steps back considers the problem and tries a new approach. You really see that by the end of the process, James has not only accomplished the goal but has gained a considerable amount of knowledge about the environment.

So how do you test drive the creation of a HTTP server in node? Like this.

'use strict';

var http = require('http');

var server = http.createServer();

exports.start = function(port, callback){
	if(!port) throw new Error('port is required.');

	server.on('request', function(request, response){
		response.end('Hello World');
	});

	server.listen(port);

	if(callback)
		callback();
};

exports.stop = function(callback){
	server.close(callback);
};
'use strict';

var server = require('./server'),
	http = require('http');

exports.request = {

	tearDown: function(done){
		server.stop(done);
	},

	setUp: function(done){
		server.start(8080, done);
	},

	testServerReturnsHelloWorld: function(test){
		var request = http.get('http://localhost:8080');

		request.on('response', function(response){
			var receivedData = false;

			response.setEncoding('utf8');

			test.equals(200, response.statusCode, 'status code');

			response.on('data', function(chunk){
				receivedData = true;
				test.equals('Hello World', chunk, 'response text');
			});

			response.on('end', function(){
				test.ok(receivedData, 'recieved data');
				test.done();
			});
		});
	}
};

exports.start = {

	testStartingTheServerWithOutAPortThrows: function(test){
		test.throws(server.start);
		test.done();
	},
	testServerRunsCallbackWhenStartCompletes: function(test){
		server.start(8080, function(){
			server.stop(test.done);
		});
	}

};

exports.stop = {

	testServerRunsCallbackWhenStopCompletes: function(test){
		server.start(8080, function(){
			server.stop(test.done);
		});
	},
	testCallingStopWhenServerIsNotRunningThrows: function(test){
		test.throws(server.stop);
		test.done();
	}
};

So if you are interested in these kinds of things, I highly recommend this series. It is worth the price of $25 per month for the over 75 episodes currently. It is the closest you are going to get to pair programming with James Shore with out shelling out some serious cash to get him to come to you.

Written on February 22, 2013