In Favour of Test Driven Development

"It sounds very mechanical, but the effect is the exact opposite. What it does is free you to write. It liberates you to write."

John McPhee on having a system to write to.

"Art is freedom; and in art, as in life, there is no freedom without law."

Martin Amis on the importance of having rules and a code of conduct.

While referring to literature, these quotes resonate strongly with my feelings on writing software (JavaScript in particular) Imposing constraints on the process leads to greater clarity of thought. In writing: correct grammar, consistent style and brevity; in code: strict lint rules, clear design and, again, brevity.

Test Driven Development (TDD) is another self-imposed constraint. The following relates to a recent StackExchange Podcast, who's hosts are not enthusiastic about its supposed benefits and have evidence to back up their assertions.

"[...] a survey of all of the studies that have been done on TDD have shown that the better the study done, the weaker the signal as to its benefit."

Greg Wilson

A small concession is made that, for the majority of projects, TDD is poorly understood and executed. In which case I agree; used for its own sake or inappropriately: TDD has little value. For example, retrofitting unit tests misses the point entirely.

Used on functional, self-contained, code (JavaScript, perhapse) TDD provides constraining benefits that free the developer to solve their problem. Thinking about and exploring a solution is never a waste of effort, writing a test is a chance to frame a problem with greater clarity. Each test further tightens focus on the task. Each test is a tangible step forward, a possible break point and a starting point for the next push.

TDD has little benefit when interacting with existing APIs (DOM interactions, UI or web services). Only code, over which the developer has complete control (new, independent functions), can be discovered creatively. Otherwise the tests are simply a collection of roll-played guesses. The do not clarify the author's thoughts and intentions, instead they can easily reinforce her misconceptions.

"The fact is that everything I've written is very soon going to be absolutely nothing - and I mean nothing."

John McPhee, again, on leaving a legacy.

If it isn't providing satisfaction or feels like a drain on progress then TDD is counterproductive. In the hands of a developer who enjoys the process it becomes valuable aid.

selfDoc.js: JavaScript that Documents Itself

Thanks to its interpreter, JavaScript can coerce functions to strings. These strings can be used as documentation. selfDoc.js is a JavaScript function that takes a function or object literal (containing a JavaScript application built in a modular style) and returns an object that describes the application's API. This can be combined with a templating tool (lm.js, perhaps) to produce a dynamic HTML document.

var test = function () {
    // A function that does nothing
  };
  test.subFunction = function () {
    // Another useless function
  };
  selfDoc("Test", test, "A test application using selfDoc.js");
  // Returns an object describing the app
  /**
  {
    appName: "Test",
    comment: ["A function that does nothing"],
    overview: "A test application using selfDoc.js"
    properties: [{
      name: "subFunction"
      comment: ["Another useless function"],
      implementation: "function () { ... }"
    }]
  }
  */

The documentation is refreshed every time selfDoc is called. selfDoc can be kept alongside unit tests or demos providing a dynamic set of documentation that is always up to date. Combine the resulting object with your favorite templating tool and you have dynamic HTML documentation. The comments are only extracted from the first continuous block (at the top of each function).

selfDoc is low on dependencies, a browser is the only requirement. selfDoc coerces the public/current-scope functions to strings, extracting their comments (FireFox/JaegerMonkey removes comments at run time, so functionality is hampered here).

There are numerous alternatives. The majority are designed to be added into a build process, such as Maven or ant. Typically they are written in a language other than JavaScript, requiring a development environment with these tools/languages installed. These may be more appropriate to large projects with established build systems.

There are alternatives that also have native JavaScript implementations, docco looks particularly tasty.

Get selfDoc.js from GitHub

Introducing LM.JS - Write HTML in JavaScript using Less Markup

Producing HTML from JavaScript is clumbersom. You're either writing out the mark-up using strings, or generating DOM nodes directly. Templating engines reduce effort and abstract away some of the inconvenience, but they have shortcomings when mixing logic with the generation of HTML/DOM (whether or not logic should be in a templating engine is another issue).

To get to the point: I saw :vana-templating, an elegant and expressive tool for Common Lisp. I wanted a JS version and built it. LM.JS is on GitHub for your perusal.

Using LM.JS

The aim was to be able to produce a :vana like data structure that could represent HTML. JS Arrays are most like Lisp's lists, objects made good key-value stores for attributes and nesting is handled by nesting the arrays. Recurse the array and evaluate each portion to a DOM node. No string parse step, only a small regular expression and little need for strict convention.

lm(['ul',
  ['li',
    ['p', 'one']],
  ['li',
    ['p', 'two']]]);

// Returns this DOM tree:
// <ul>
//   <li>
//     <p>one</p>
//   </li>
//   <li>
//     <p>two</p>
//   </li>
// </ul>

Add some attributes and nest within text:

lm(['p', 'Some text ', ['em', 'emphasised'], ' and a ', ['a', {href: '#nest'}, 'link']]);

// <p>Some text <em>emphasised</em> and a link</p>

What about logic? As the Array is JS, simply build the array using JavaScript's logic, no need to implement a mini-language.

lm(['ul', (function () {
  var rtn = [], i;
  for (i = 0; i < 10; i += 1) {
    rtn.push(['li', (i + ' item')]);
  }
  return rtn;
}())]);

// <ul>
//    <li>0 item</li>...

lm.js differs from templating solutions as JavaScript logic can be used in-line.

lm.render(function (obj) {
  return lm(['p', obj.text]);
}, {text: 'hello'});

// <p>hello</p>

Performance

The aim of this version was to gain the expressive markup and feel of vana in JavaScript. While performance is an issue, it was not the sole aim to be fast at the expense of ease of use.

This templating performance test on JSPerf has LM.JS coming out in last place, 1% behind mustache.js. In the coming weeks I aim to place LM.JS higher up the list. However, the techniques used to satisfy this particular test may not be desirable in the long run. Using more intelligent caching and priming those caches are techniques that could be employed. On the other hand, creating a cache of each element, or element group, has its own overhead in execution speed and memory. "Cold start" performance matters too and memory is a major factor on mobile.

One improvement may be to use string concatenation with innerHTML as opposed to the current DOM elements attached to document fragments (a technique covered in Nicholas Zaka's Google tech talk).

LM.JS - The pros

  • Some of the style from :vana in JavaScript
  • No string parsing, lexing or heavy validation needed
  • Uses native JS Arrays as core data type
  • Provides a very flexible way to deal with DOM generation
  • Requires fewer characters to produce well formed HTML, smaller JS payloads too
  • Can be used as a markdown like tool for quickly producing HTML

LM.JS - The cons

  • Slower than a templating engine, although not by a lot and dependent on the benchmarking rules
  • JS is not as syntactically suited to list processing as Lisp, vana is more elegant
  • Templates are tightly bound to JS, it would be hard to replicate the exact logic in another environment
  • Only works in browser, e.g. node.js would require a rewrite using string concatenation

Next Steps

Building up LM.JS to cover more traditional templating functionality and move up the performance table at JSPerf. Just how far up is a difficult question, optimisation could be handled by the user in a case-by-case basis.

Comment/Vote at Hacker News or Reddit:JavaScript

Functional, TDD JavaScript (influenced by Haskell, Lisp, Erlang...)

Having developed a taste for Functional Programming (FP), I've found that there are many aspects that make building software in a TDD style easier. Functions are the basis of FP, a function that takes arguments and returns values is easy to test. If this function is side effect free (i.e. doesn't effect the program from outside its internal scope; has no infulence on, or effect from, state), you can be confident that that function will always work the way your tests expect.

// SIDE EFFECT FREE
fAddOne = function (num) {
    return (num + 1);
};

test("fAddOne", function () {
    eq(fAddOne(0), 1);
    eq(fAddOne(1), 2);
});

// SIDE EFFECT DEPENDENT
obj = {
    num: 0,
    pAddOne: function () {
        this.num += 1
    }
};

test("pAddOne", function () {
    obj.num = 1;
    obj.pAddOne();
    eq(1, obj.num);
});

In the above examples, fAddOne will work anywhere within the app, pAddOne is side-effect based and needs to be called within the scope of an object with a num property. If a refactor in needed, fAddOne, and its tests, can move arround or change applications. pAddOne has some requirements that unit tests don't describe as easily, refactoring will be a little trickier. In effect pAddOne's tests are testing side effects not functionality.

However, the example oversimplifies the problem. In reality side effects are essential, I/O can't be avoided. In (browser based) JavaScript this is usualy in the from of DOM API interaction. In GB.js I attempt to keep a CYOA/GameBook engine independent of side effects, in the demo, DOM building and events are kept to a minimum and try not to overlap. This is fine for individuals working to their own requirements. Teams have different problems; in JS, side effects are easy and the syntax encourages them, in most cases it's easier to just cave in. It may even get work done faster (at first). But, from a Unit Testing perspective quality drops or, at least, refactoring become trickier.

FP is a tool industry could gain more of value from, as does by adopting its features and principals (closures, currying, recursion etc...). The problem for me is blending Object Oriented (OO) and FP styles with TDD. Refactoring and reuse are important, and when a shortcut is made with OO then quality can suffer.

Another gain with FP can be shorter code, but when using TDD with loosely typed languages (JS, Erlang, Lisp etc...) type checking causes length to creep up. While rewriting some of excersises from "The Little Schemer" (with TDD JS) it became apparent that if you want high confidence in the Unit Tests then a lot of type checking happens. This is why I have a set of type checking functions that I use constantly. So, if I'm typechecking a lot am I just re-inserting the type safty of a strongly typed language?

Looking at other FP languages, Haskell currently satisfys me the most. Sepparating  side effects into Monads (I'm still in the process of learning this concept) and using strong typing (and a compiler) feels like it provides real confidence in quality. In fact, by having to decide types in the function definitions there is no need to have tests to cover type safety. So the extra "boiler plate" (that isn't even required by the compiler) can reduce the overall lines of code typed.

I'm hoping that by delving deeper into Haskell's approach I can get a clearer steer on how to construct functional JavaScript applications.