I’d like to introduce a new software development technique that is reminiscent of TDD, but at a higher, feature-oriented level. It’s based on the observation that features have both a confirmational and functional aspect. In brief summary:

  • The functional aspect is the mechanism used to bring about a result
  • The confirmational aspect is what lets the user know that their desires and goals for using the software have been met.

The key to Feature TDD is to build the confirmational aspect first, which then serves as the ‘test’ for the subsequently developed functional aspect. Here are the steps I’ll be using:

  1. Capture vision and intent through literal confirmational representation
  2. Pin output of #1 via test
  3. Make vision templates data-driven
  4. Extract data model and create functional stubs
  5. Implement functional behavior
  6. Use techniques #1 and #2 to create functional user interfaces
  7. Link #6 to functional behavior created in #5

In the post, I’ll cover steps #1-#4.

This simple example is going to be focused on a very simple event registration web site for our hypothetical customer Larry. I’m going to be using node.js for this exercise. I have playing around with it for the past few months, and it has been a very compelling technology thus far.

Capture Vision and Intent


Conversation

The goal for this step is to work with the customers and users to agree upon a set of interfaces and indications that will demonstrate that their vision or intent has been satisfied by the system (you can also think about it as answering “what will will make them happy?”). This is very similar to Specification by Example or BDD, except that the focus is solely on the confirmational aspects of the features.

For more detailed specific techniques for engaging the customer, check out my post here, and also Gojko Adzic’s interesting new technique of effect-maps. A real conversation with Larry would expose a lot of in-depth contextual answers.

Right now, I want to show the end-to-end technique so I’m going to keep this extremely simple and say that Larry really just wants to see a list of names of those who have registered for his event.

Literal Templates

I’ll be using the templating engine jade to quickly work up the presentation. It’s format is fairly simple, and I like it because it can yield a visualization very simply and quickly. Here is the literal template for the guest list:

h1 Guest List for Larry's Event
ul#guests
  li Bob
  li Sally
  li Tim
  li Joe

And here is the node.js server-side code for rendering this (I’m also using express and connect, a web framework for even simpler application routing and server) :

express = require('express'),
connect = require('connect'),
app = express.createServer();
app.set('views', __dirname + '/views/');

app.get('/guests', function (request, response) {
  response.render('guests.jade',
          { layout: false, });
});

app.listen(3003);
console.log("Listening on port 3003");

This renders as:

I won’t be going any further than running on my local dev machine at this point, but with this simple, small fragment, there is enough to begin learning (by doing) about (continuous) deployment.

Also, with my tester hat on I notice that Larry probably wants to prevent empty names from showing on his list. For now, I’ll just record this observation.

Moving on to the next step I’ll focus on the guest who registers. Again, after deferring to Larry, I’ll assume we can  keep this very simple:

h1 You Are Registered for Larry's Event
p Thanks for registering Bob! We look forward to seeing you.

Another get method is added to the code and a Render method can be refactored out:

app.get('/guests', function (request, response) {
  Render(response, 'guests');
});

app.get('/guests/confirmation', function (request, response) {
  Render(response, 'confirmation');
});

function Render(response, template) {
	var options = { layout: false };
	response.render(template + '.jade', options);
}

The new method will soon be changed to app.post('/guests', but initially we want the resource to be directly accessible so the outcome is demonstrable (some of the tooling I’ll show later makes this step unnecessary). This renders:

At this point, these confirmation ‘tests’ have been validated by Larry, and we can consider that the output is now the ‘gold standard’ of captured intent and vision.

Pin the Standards with Tests


The output is pinned so that the next step of making the templates data-driven can be made with the confidence that the standard has not changed. Fixing the html output from jade is trivial from the command line:

$ jade guests.jade confirmation.jade
  create : guests.html
  create : confirmation.html

There are a number of node.js testing frameworks, I’m going to use Vows to write the tests. I rolled my own quick http-client module which is picked up at var client = require('./http-client'). There’s probably a good node module for this that I just haven’t found yet.

Here is the basic structure of the test, the client http response is compared against the spec standard:

topic: function () {
  client.get('/guests', this.callback);
},
'should return the standard':
  function (err, httpResponse) {
    fs.readFile('./html standards/guests.html', 'utf-8',
      function(err, standard) {
        assert.isNull(err);
        assert.equal(httpResponse.data, standard);
    });
  }

Adding both tests and refactoring out the common aspects leaves the initial test file as:

var vows = require('vows'),
assert = require('assert'),
fs = require('fs');

var client = require('./http-client');

vows.describe('Event Registration Confirmation')
  .addBatch({
    'Guest List Confirmation': getContext('guests',
      function () {
        client.get('/guests', this.callback);
      }),
    'Registration Confirmation': getContext('confirmation',
      function () {
        client.getPost('/guests', this.callback);
      })
  }).export(module);

function getContext(standard, httpGet) {
  return {
    topic: httpGet,
    'should return the standard': function (err,
      httpResponse) {
        fs.readFile('./html standards/' + standard + '.html',
          function(err, response) {
            assert.isNull(err);
            assert.equal(httpResponse.data, response);
      });
    }
  }
}

The signature of the registration confirmation is changed to be a post operation, and running the tests (the server app also needs to be running too) yields the following:

Marty.Nelson@Laptop-Mnelson7 ~/Larry's Event/tests
$ vows --spec confirmation_specs.js

♢ Event Registration Confirmation

  Registration Confirmation
    ✓ should return the standard
  Guest List Confirmation
    ✓ should return the standard

✓ OK » 2 honored (0.119s)

Data-Drive the Templates


With tests in place, the templates can be safely changed to be data driven. There are two parts to the change, the first is to pull the literal data values out of the templates and into the server code (passing them into the template):

app.get('/guests', function (request, response) {
  Render(response, 'guests',
    {guests: ['Bob', 'Sally', 'Tim', 'Joe']});
});

app.post('/guests', function (request, response) {
  Render(response, 'confirmation', {guest: 'Bob'});
});

function Render(response, template, locals) {
	var options = { layout: false };
	if(locals) { options.locals = locals; }
	response.render(template + '.jade', options);
}

And the second is to parameterize the jade templates:

h1 Guest List for Larry's Event
ul#guests
  - each guest in guests
    li= guest

and

h1 You Are Registered for Larry's Event
p
  | Thanks for registering #{guest}!
  | We look forward to seeing you.

The tests still pass (I’ve removed the more verbose –spec option):

$ vows specs.js
··

? OK » 2 honored (0.051s)

Functional Stub


Next, I’ll push the literal code into an Event stub. Because node.js code should be non-blocking, the calls into the Event follow the pattern of supplying a callback, and that callback (by convention) has the signature (err, data):

Event.getGuests(function(err, data) {
    if(err) { console.log('we got an error', err'); }
    console.log('return data:', data);

I’ll start by extracting it from the server get methods:

var Event = {};
Event.getGuests = function(callback) {
  callback(null, ['Bob', 'Sally', 'Tim', 'Joe']);
};
Event.register = function(name, callback) {
  callback(null, 'Bob');
};

app.get('/guests', function (request, response) {
  Event.getGuests(function(err, data) {
    Render(response, 'guests', {guests: data});
  });
});

app.post('/guests', function (request, response) {
  Event.register(request.param('name'), function(err, data) {
    Render(response, 'confirmation', {guest: data});
  });
});

Then I’ll move the Event code into another file (under the lib folder):

exports.getEvent = function () {

  return new Event();

  function Event() {
    this.getGuests = function(callback) {
      callback(null, ['Bob', 'Sally', 'Tim', 'Joe']);
    };
    this.register = function(name, callback) {
      callback(null, 'Bob');
    };
  }
}

And require it as a node module in the server code:

var Event  = require('./lib/event').getEvent();

Now I want to use traditional TDD to flush it out. Oddly, I need to backfill tests at the Event module level that are coming after the stub. Another approach would have been to build up the identical interface to what was extracted in the server code. After the first few cycles, the tests looks like:

var vows = require('vows'),
    assert = require('assert')

var Event = require('../lib/event').getEvent();
vows.describe('Event').addBatch({
  'An Event': {
    'when asked for guests': {
      topic: function () {
        Event.getGuests(this.callback);
      },
      'should return the standard guests':
        function (err, guests) {
          assert.deepEqual (guests,
            ['Bob', 'Sally', 'Tim', 'Joe']);
        }
    },
  'when registering Bob': {
      topic: function() {
        Event.register('Bob', this.callback);
      },
      'should return Bob':
        function (err, guest){
          assert.equal(guest, 'Bob');
        }
     }
  }
}).export(module);

Running these tests yields:

$ vows --spec event_specs.js

♢ Event

  An Event when asked for guests
    ✓ should return the standard guests
  An Event when registering Bob
    ✓ should return Bob

✓ OK » 2 honored (0.013s)

Next Steps


I’m well setup to  flush out the functional aspects of the Event class. In the next post, I’ll do this and then work my way back to the Registration interface to complete the applicaton.

You can view the code at github. If you know how to view history, the commit for the first part is labelled.

P.S. Thoughts on Tooling…

In doing a few of these templates, I created a web tool intended to be a rapid collaborative environment for this technique:

The output in the upper-right updates in real-time as the template is modified. The “save template” button saves the template to the project and also creates the static html ‘standard’ representation. The CSS area is not currently functional, but you can see how this be used to provide design aspects.

I have also been working on making these editors collaborative so that you could have multiple team members working up the confirmation feature (ideally with the customer or product owner) at the same time.

About these ads