Agile Zone is brought to you in partnership with:

I am a programmer and architect (the kind that writes code) with a focus on testing and open source; I maintain the PHPUnit_Selenium project. I believe programming is one of the hardest and most beautiful jobs in the world. Giorgio is a DZone MVB and is not an employee of DZone and has posted 638 posts at DZone. You can read more from them at their website. View Full User Profile

CoffeeScript: a TDD example

08.09.2011
| 8782 views |
  • submit to reddit

CoffeeScript is a language building an abstraction over JavaScript (as the similar name suggests.) It is an abstraction over the syntax of JavaScript, not over its concepts: the language is still based on functions as objects which may bind to other objects, and prototypical inheritance.

CoffeeScript favors best practices of JavaScript by transforming abstractions you would have written anyway, or borrowed from a framework, into language concepts for maximum conciseness. It has a compilation step - as every language which must compile to a lower-level one, like C or Java.

Since cowboy coding is not my preferred way to work, I prepared a Test-Driven Development example by using jsTestDriver. In this article, you get two things: a CoffeeScript introduction, and the tools for unit testing it (and consequently, how to TDD with CoffeeScript).

The infrastructure

The basic structure consists of two folders: src/ and lib/; remember the compilation step. We'll put .coffee files into src/ and compile them to .js equivalents in a symmetrical tree in lib/.

We add also a jsTestDriver.conf file to tell the unit testing framework all the files to load, which are only the "binary" .js scripts:

server: http://localhost:4224

load:
  - lib/*.js

Compiling

This is the first version of the test I've managed to write, fizzbuzztest.coffee. It is a tautology that should always pass:

mytest = () -> assertEquals(1, 1)

tests = {
  "test1is1": mytest
}

TestCase("tests for fizzbuzz kata", tests)

You see here that functions are still first class objects, but only anonymous functions are supported. CoffeeScript is a Python/Ruby-like language without semicolons, and there are some affinities and common background with the latter language.

I still use the old syntax for calling functions for now, although parentheses can be omitted in many cases. CoffeeScript is conservative, and even accepts semicolons if you want to write them.

I compiled this script with coffee -o lib/ -c src/. fizzbuzztest.js is the result:

(function(){
  var mytest, tests;
  mytest = function() {
    return assertEquals(1, 1);
  };
  tests = {
    "test1is1": mytest
  };
  TestCase("tests for fizzbuzz kata", tests);
})();
The global namespace is not touched by default, and var keywords are automatically introduced to preserve it. When I later needed the global namespace, I wrote:
this.fizzbuzz = /* ... function definition ... */
this in this case is the window object, or the other global object where you execute the code.

Running

To run the test, we must initialize the test driver (only once):
jsTestDriver java -jar JsTestDriver-1.3.2.jar --port 4224

jsTestDriver will now listen at localhost:4224. Load this URL in your browser and capture it by clicking on the link. Tests will be executed inside the browser when requested: for more details see the related article.

Every time you want to run the tests, execute from the command line:

java -jar JsTestDriver-1.3.2.jar --tests all

This is the complete history of my kata. Here is the final version of the code (spoiler alert!), with support for addition of other factors than 3 or 5. The code is probably uglier than average, but it compiles:

tests = {
  "test ordinary numbers are unchanged": ->
    assertEquals(1, fizzbuzz(1))
    assertEquals(2, fizzbuzz(2))
    assertEquals(4, fizzbuzz(4))
    assertEquals(142, fizzbuzz(142))
  "test multiples of 3 become fizz": ->
    assertEquals("Fizz", fizzbuzz(3))
    assertEquals("Fizz", fizzbuzz(6))
    assertEquals("Fizz", fizzbuzz(9))
  "test multiples of 5 become buzz": ->
    assertEquals("Buzz", fizzbuzz(5))
    assertEquals("Buzz", fizzbuzz(10))
  "test multiples of 3 and 5 become fizzbuzz": ->
    assertEquals("FizzBuzz", fizzbuzz(15))
    assertEquals("FizzBuzz", fizzbuzz(45))
}

TestCase("tests for fizzbuzz kata", tests)
newRule = (word, divisor) ->
  (number) ->
    return word if number % divisor == 0
    ""

newFizzBuzz = (rules) ->
  (number) ->
    result = ""
    concatenation = (rule) ->
      result = result + rule(number)
    concatenation rule for rule in rules
    return result if result
    number

fizzRule = newRule("Fizz", 3)
buzzRule = newRule("Buzz", 5)

this.fizzbuzz = newFizzBuzz([fizzRule, buzzRule])

Comments on the experience

CoffeeScript offers a shorter syntax, which presents a bit of a learning curve but not a steep one. I went through the whole example in 1 hour (I already knew how to use jsTestDriver, however.)

Syntax shapes how you write code by making some things easier: I found myself using higher-order function which create other ones more often, since creating a function now is just a matter of putting -> before some lines of code.

Variable naming is also simpler as you just have to think of the name, not about var or polluting the scope. More time to dedicate to design, and less to language issues. Some one-liner like the instruction if expression are handy but not essential, and are there due to Ruby inspiration.

There's even more to discover in CoffeeScript, such as the options for the binding of functions which helps not to lose the reference to this. However, the question is if all this convenience has more value than the time spent to learn a new language and adding infrastructure to make it work - the compiler, the build hooks and the parallel tree to ignore in your version control system.

Published at DZone with permission of Giorgio Sironi, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)