Basic test for HTTP client-server process

This post is a reflection on the Simple Node.js Server lessons (episode 9-16) and Integration Testing a Node.js Web Server lesson by James Shore.

Complementary processes

The lessons present - among other things - a test driven approach of creating a simple Node.js server, and in the course of the lessons you are given a lot of help to understand the server-client process in general. At the end you get a test, that checks the server side: whether the server is created, if it gives response, and finally if the server is closed.

While I was thinking about this approach, and tried to think over the process, the following idea came into my mind:

It seems to me that the server and client side are somehow complementary, and therefore if you want to create a test for the server, the test can play the role of the client side. The idea was that in a complementary process like this the testing and the tested can change their position without having to rewrite the code much. I decided to test it out.

(For the test I used the nodeunit plugin for Grunt. If you interest yourself in its working, check my post about basic nodeunit test.)

The original test

Here comes the original test:

(function () {  
    "use strict";

    var server = require("./server.js");
    var http = require("http");

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

    exports.tearDown = function(done) {
        server.stop(function() {
            done();
        });
    };

    exports.test_respondsToRequests = function(test) {
        httpGet("http://localhost:8080", function(response, responseText) {
            test.equals(response.statusCode, 200, "status code");
            test.equals(responseText, "Hello World", "response text");
            test.done();
        });
    };

    function httpGet(url, callback) {
        var request = http.get(url);
        request.on("response", function(response) {
            var responseText = "";
            response.setEncoding("utf8");

            response.on("data", function(chunk) {
                responseText += chunk;
            });
            response.on("end", function() {
                callback(response, responseText);
            });
        });
    }
}());

And the original server side:

(function() {  
    "use strict";

    var http = require("http");
    var server;

    exports.start = function(portNumber, callback) {
        server = http.createServer();
        server.on("request", function(request, response) {
            response.end("Hello World");
        });
        server.listen(portNumber, callback);
    };

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

The test first call the server.start function, then tries to get a response from the server (and tests if the response is equal to "Hello World"), and finally, call server.stop function. The server side has a start function, that creates a Nodejs server, and a stop function, that closes the server. The callback functions assure that after a task is achieved on the server side, the test code will be called back and continues its running. The server.on function is a Node.js event emitter, and the attached function is executed when the event (the request) is emitted. The same applies for request.on and response.on event emitters in the test code.

Changing the positions

I rewrote the codes so as to test the client side. Now the test code creates the server, then tests if there is a request, checks the response and finally closes the server.

Here is the modified test:

(function() {  
    var client = require("./client.js");
    var http = require("http");
    var portNumber = 8080;
    var server;

    exports.setUp = function(done) {
        function start(portNumber) {
            server = http.createServer();           
            server.listen(portNumber);
            done();
        }
        start(portNumber);
    };

    exports.test_request = function(test){
        var requestText = "Hello World";
        server.on("request", function(request, response) {
            response.end(requestText);
        });

        client.requests(function(responseText){
            test.equals(requestText, responseText, "Test request");
            test.done();
        });
    };

    exports.tearDown = function(done) {
        stop = function(callback) {
            server.close(callback);
            done();
        };
        stop();
    };  
}());

And the modified client side:

(function () {  
    var http = require("http");

    exports.requests = function(callback) {
        var url = "http://localhost:8080";
        var request = http.get(url);
        request.on("response", function(response) {
            var responseText = "";
            response.setEncoding("utf8");
            response.on("data", function(chunk) {
                responseText += chunk;
            });
            response.on("end", function() {
                callback(responseText);                     
            });
        });
    };
}());

As you can see the codes are modified barely. I had to move the setUp and tearDown Nodeunit functions to the server side (the former runs before the test, and the latter runs after the test. For details see the Nodeunit documentation). The test focuses on the request, but it tests the same as the original code: if you get the response ("Hello World") that is sent by the server.

Summary

The test succeeded, it was possible to change the position of the test and the tested, and to create a test on the server side with a little change in the codes. You can download or clone this project from here.



comments powered by Disqus