Sunday, August 28, 2011

BDD Testing Example with Node.js and Jasmine

In Node.js at least, using jasmine is pretty straightforward.  We can use standard Node.js require(.) semantics to include the modules we're testing.  In the prior examples, we defined a couple of modules, one that defines a random number generator and another that returns the current date.  Here, we're going to write some simple specifications we can use to test the services.

First, in the project, create a spec directory.  I created it right next to the lib directory, which holds my source files.  Once that's in place, you can create your specifications. The first specification, the date specification, tests the date service.  You can include the date service source files with using require(.):

var date = require('../lib/date').date;


describe('date specification', function(){


  it('should return a valid value', function(){
    var generator = new date.Generator();
var value = generator.generate();
expect(value).not.toBeNull();
  });


it('should return the current date', function() {
var generator = new date.Generator();
expect(generator.generate().toString()).toEqual(new Date().toString());
});


});


This, we can run this with jasmine-node by executing the command line 'jasmine-node spec' in the directory containing the spec subdirectory.  We can then create an additional suite of tests for the random service:


var random = require('../lib/random').random;


var SEED = 10;
var LOW_SEED = 2;
var MED_SEED = 50;
var HIGH_SEED = 1000;


var evaluator = function(seed) {
var generator = new random.Generator(seed);
var value = generator.generate();
expect(value).not.toBeNull();
expect(value).toBeGreaterThan(-0.001);
expect(value).toBeLessThan(seed);
};


describe('random specification', function() {

it('should return a value between 0 and the seed', function() {
evaluator(SEED);
});

it('should work with a variety of seed values', function() {
evaluator(LOW_SEED);
evaluator(MED_SEED);
evaluator(HIGH_SEED);
});

});

Now, when we execute jasmine-node, we see this (note, I'm using verbose mode here):

prompt> jasmine-node --verbose spec
Started
....


Spec date specification
Spec random specification
Finished in 0.004 seconds
2 tests, 14 assertions, 0 failures

Now we've successfully integrated our jasmine-node tests into our repository, and we can execute them from the command line.  The next step is to integrate them with a continuous integration system like Jenkins.

Saturday, August 27, 2011

Testing JavaScript with Jasmine and Node.js

To extend our previous example, there's a couple of tools you can use to test your code in a Node.js environment.  One of the most stable, and the one I'm planning to use, is jasmine-node, installable via npm or downloadable from git.

First, to install using npm, you'll need to use the following command-line (after, of course, you've installed npm itself):

prompt > sudo npm -g install jasmine-node

You'll get output like this:


/usr/local/bin/jasmine-node -> /usr/local/lib/node_modules/jasmine-node/bin/jasmine-node
jasmine-node@1.0.6 /usr/local/lib/node_modules/jasmine-node 
└── coffee-script@1.1.2

Sweet! You are now able to do behavioral-based testing of your Node.js code!  Here, on m mac, the -g  option was required.  Anyway, if you look at the output, you'll notice that you now have a command jasmine-node that's installed in /usr/local/bin (which is why you needed to run this via sudo).  Assuming that's in your path, you can test your installation:

prompt> jasmine-node

Which should give you the output:

USAGE: jasmine-node [--color|--noColor] [--verbose] [--coffee] directory

Options:
  --color      - use color coding for output
  --noColor    - do not use color coding for output
  --verbose    - print extra information per each test run
  --coffee     - load coffee-script which allows execution .coffee files

So if you see this, you know you've got it installed.  So let's do a bit more testing to ensure everything's looking good.

If you go to the git site where jasmine-node is hosted, you'll see a reference in the readme to a javascript test file.  You'll need to drop that into a directory named spec, and then we should be ready to test.  I'm using both a JavaScript and a CoffeeScript file, provided by Miško Hevery, jasmine-node's creator.

Once you've copied both of those files into a spec directory, cd to the directory directly above the directory you just created, and you can execute the tests like this:

prompt > jasmine-node spec

This will give you the following output:

Started
..

Finished in 0.011 seconds
1 test, 3 assertions, 0 failures

But wait, where's the CoffeeScript test? It's there, but you need to use the --coffee flag to get it to execute:

prompt > jasmine-node --coffee spec

Then you'll get what you expect:

Started
...

Finished in 0.013 seconds
2 tests, 4 assertions, 0 failures

That's it! You've now installed jasmine-node and you're ready to start testing your work.  In the next post, I'll go over how to import and test code from the random date server example.  Further upcoming posts will include using Node.js and HTML5 to implement an RPC infrastructure over HTTP with rich internet clients.  Prior to that, I'll explore how you can structure these kinds of projects in Git, hopefully including integration with Jenkins.

Wednesday, August 17, 2011

Design Primitives in Javascript: Pulling it Together

Now to pull together the components into a functional random number and date server!  I'm certain to get loads of venture funding for this highly in demand service, something to look forward to.

Anyway, at this level, I maintain two modules.  The first, the Node.js entry point, starts the server with callbacks defined in the main module.  To be precise, the first file is not actually a module as we've defined modules so far.  Rather, it's a simple script calling the appropriate bootstrapping methods:

(server.js)

var net = require('net');
var main = require('./main').main;


net.createServer(main.connection_listener)
  .listen(1337, "127.0.0.1", main.event_listener);

That's it! Pretty cool.  This just spawns a server on port 1337 using listeners defined in main, where the bulk of the communication implementation resides:

(main.js)

var version = require('./version').version;
var date = require('./date').date;
var random = require('./random').random;


exports.main = function() {



  // This is a private class object used to build 
  // the response generator needed based on the 
  // type of request. The dispatch(.) method takes 
  // a string and then calls the corresponding 
  // factory method based on the predefined module 
  // interfaces (see the previous two posts for 
  // more information on  those interface 
  // definitions).  If someone  requests an 
  // operation we don't recognize, we create an
  // anonymous class object implementing 
  // the generator interface.
  var _processor = function() {

    var SCALE = 10;
    var DATE = 1;
    var RANDOM = 2;
    var UNKNOWN = 3;

    var parser = function(cmd) {
      if (cmd.match(/date/ig)) return DATE;
      if (cmd.match(/random/ig)) return RANDOM;
      return UNKNOWN;
    }


    this.dispatch = function(cmd) {
      var generator;
      switch(parser(cmd.toString())) {
        case DATE:
          generator = new date.Generator();
          break;
        case RANDOM:
          generator = new random.Generator(SCALE);
          break;
        default:
          generator = new function() {
            this.generate = function() { 
              return 'undefined token submitted.';
            }
          }
      }
      return generator.generate();  
    }

  }



  // This is a private function building a version 
  // string using information defined in the version
  // module.
  var _build_version_string = function() {
    return version_string = version.major + '.' 
      + version.minor + '.' 
      + version.patch + ' - '
      + version.status;
  }



  // The server entry point.  We write output to the 
  // requestor socket as well as debug information 
  // to the console, first. We then define a handler
  // for submitted data using our private processor
  // class.  The handler processes the request
  // and emits a bit more status information to the
  // console.  Finally, we drop binding status to the
  // console and return.
  var _entry_pt = function(socket) {

    socket.write('date & random server: ' 
      + _build_version_string()
      + '; enter command\r\n', 
      'ascii', 
      function() { 
        console.log('...welcome sent to ' 
        + socket.remotePort 
        + '@' 
        + socket.remoteAddress); 
      });

    var processor = new _processor();


    socket.on('data', function(data) {
      console.log('data received: "' + data + '"');
      var retval = processor.dispatch(data);
      console.log('response: ' + retval);
      socket.write(retval + '\r\n');
    });


  }


  var _event_listener = function() {
    console.log('...server has been bound.'); 
  }


  // The module signature implementation.
  return {
    connection_listener: _entry_pt,
    event_listener: _event_listener
  }
}();


Note the comments above describing what's what in the implementation module.


Now, to run this on the command line type:


$ node lib/server.js


This'll bootstrap the server and kick it off, sending confirmation messages to the console.  To connect, use telnet:


$ telnet localhost 1337


And you'll be greeted by the smarmy welcome screen:


Connected to localhost.
Escape character is '^]'.
date & random server: 1.0.0 - gold; enter command

From here, you can type random or date.  The response will be either a randomly generated number or the current date.  That's it!  Now, I would generally not do something even this trivial without some kind of unit test support.  Next, I'm going to delve into how you can integrate Jasmine with Node.js to do test driven development.

Thursday, August 4, 2011

Design Primitives in Javascript: Example Modules, Classes, Functions

Okay, so we defined the modules I'm using in the last post; here, I'll show you the code associated with the version, date, and random modules, and explain what it's doing.

We'll start with the version module as it's the simplest:

(version.js)

exports.version = {
major: 1,
minor: 0,
patch: 0,
status: 'gold'
}

That's it.  Notice, this is an implementation of the versioning interface we defined previously.  We're exporting the version information in an object literal, in the version property, so you reference it from including files using require like this (this assumes side-by-side installation of library files with the server script):

var version = require('./version').version;

The date generator is a little more complex, but not very:

(date.js)

exports.date = function() {


  var _date_generator = function() {
    this.generate = function() {
      return new Date().toString();
    }
  }

  return {
    Generator: _date_generator
  }
}();

Note I've used the module pattern here.  That allows me to define the Generator class based on the private implementation class named _date_generator.  I include and use this class this way:

var date = require('./date').date;
...
var generator = new date.Generator();
var date = generator.generate();

Finally, the random number generating class, using a nifty private variable:

(random.js)

exports.random = function() {

  var _random_generator = function(scale) {

    var _scale = scale;

    this.generate = function() {
      return Math.floor(
        Math.random() 
        * _scale)
      .toString();
    }
  }

  return {
    Generator: _random_generator
  }
}();

Again I use the module pattern, and this time as an added bonus, I use a private variable (i.e. _scale, in the _random_generator class).  Include and use like this:

var random = require('./random').random;
var SEED = 10;
...
var generator = new random.Generator(SEED);
var randomNumber = generator.generate();

And finally, notice that both generator classes adhere to the Generator interface defined in the previous post!  Anyway, that's it for the modules that do the domain work of the service.  Next I'll go into how to get this to work with the network communication plumbing in Node.js.