Thursday, October 27, 2011

Learning CoffeeScript With Unit Tests


CoffeeScript is a language that transcompiles to JavaScript and has syntax that looks more like Ruby or Python. In other words, it allows you to write JavaScript without writing JavaScript and with a lot less code. The generated JavaScript code even runs as fast, if not faster, than if you had written pure JavaScript.

I don't mind writing pure JavaScript but since I really like Python and Ruby, I figured I'd give a try since the syntax is familiar. CoffeeScript can even make jQuery even easier to write than it already is.

My Coworker Danny turned me on to using Koans to learn Ruby, and when he mentioned there's a CoffeeScript version, I immediately jumped on it.

Koans can aid in learning a new programming language. They're a Behavior Driven Development approach that turn learning into a kind of game where you fill in the blanks to make tests pass.

A CoffeeScript Koan consists of CoffeeScript code with Jasmine unit test files. The tests look like:
 # Sometimes we will ask you to fill in the values
 it 'should have filled in values', ->
   expect(FILL_ME_IN).toEqual(1 + 1)
You then compile the file (in this case AboutExpects.coffee) using the command-line coffee tool:
 $ coffee -c AboutExpects.coffee
which in turn creates the corresponding JavaScript file (in this case AboutExpects.js). If we were just writing CoffeeScript code, we could then run the .js file in a Web Browser, as usual. Or even execute the JavaScript file without a browser, if you have node.js installed, which we'll do in a minute:
 $ node yourFile.js
However, since we're doing Koans, we'll load the file using the bundled "KoansRunner.html" file, which loads our compiled .js file using the Jasmine Behavior Driven Development framework. This let's us know if our tests passed or failed.

Let's start by installing the CoffeeScript and the command-line compiler. Note that modern Ruby on Rails installs should already have CoffeeScript. For this tutorial I've only tested the following in Ubuntu Linux, but it should work for most Unix-like Systems. But you're always free to check for bundles for your system if you want to make it a little easier on yourself.

The coffee command-line tool is dependent on Node.js, so we'll need to install that first. Currently, the best way to install Node is to compile it yourself. So download it via the tarball or github repository, then run the usual:
$ ./configure && make && sudo make install
Now that Node is installed, we'll install the Node Package Manager (NPM):
 $ cd /tmp
 $ curl -O http://npmjs.org/install.sh
 $ sudo ./install.sh
Now you can install the coffescript command with:
 $ sudo npm install -g coffee-script
Once installed, typing coffee should show an interactive prompt like:
 coffee>
Type Ctrl-D to exit.

You're now ready to install the CoffeeScript Koans from the github repository. Assuming you're working from ~/src/:
 $ mkdir ~/src/koans
 $ cd ~/src/koans
 $ git clone https://github.com/sleepyfox/coffeescript-koans.git
 $ cd ~/src/koans/coffeescript-koans/
In the above screenshot image in this article, you can see I've split my Linux screen in half, with the top half a Web Browser running KoansRunner.html. The bottom half is AboutExpects.coffee open in Vim.

Note that the answers to the first two Koans are already filled out in my example. They're so overly simplistic that just by understanding those two answers you'll have learned as much as you otherwise would have, so don't feel cheated.

So now it will be your job to open koans/AboutExpects.coffee and fill in a test method with what you assume is the correct answer. Then compile it with:
 $ coffee -c koans/AboutExpects.coffee
and move the generated JavaScript file to the lib/koans directory and reload the KoansRunner.html file to see the new test results. Once you complete all the Koans in AboutExpects.coffee, you'll move onto the next Koans file, AboutArrays.coffee, and so on.

There are a couple of tips for making compiling and reloading easier. One tip is that at the root of the Koans directory, you can run a command called "cake", which was installed earlier during the coffee install process. You can verify this with:
 $ file $(which cake)
 /usr/local/bin/cake: symbolic link to `../lib/node_modules/coffee-script/bin/cake'
 $ file -L $(which cake)
 /usr/local/bin/cake: a node script text executable
Running:
 $ cake build
will compile all of the tests in the koans/ directory and automatically cp them to the lib/koans directory. This allows you to add a Vim keyboard mapping to your ~/.vimrc file, such as:

    nmap <Leader>c :!cake build<CR>

which lets you recompile the Koans by simplying typing \c in Vim. The "Leader" key on my system is a backslash, YMMV. Just make sure to run the shortcut from the root of the Koans directory so it can find the build file.

If you have Ruby and RubyGems installed, there's an even easier way to do this, using a "watch" command. The coffee command-line tool comes with an option called '-w', which watches for modified timestamps of files and then re-compiles the files automatically when they change. And the Koans come with a nice extension to this by including Ruby files called koans-linux.watchr, koans-win.watchr and koans-mac.watchr, respectively. To use it you need to install the watchr gem:
 $ gem install watchr
Now, if you were on a Linux machine, say, you would run this in a terminal:
 $ watchr koans-linux.watchr
Now you can edit and save your CoffeeScript koans and just hit refresh in your browser. The cake build step is taken care of automatically by the watchr!

Another thing that's nice to be able to do, is check our CoffeeScript code for syntax errors before we try to run it. This is known as "linting". The coffeescript command-line tool comes with an option called "-l" that lets you lint your files, but only if you have jslint installed first. So install it with:
 $ npm install jslint
At this point, you're breezing through the Koans and you start learning enough CoffeeScript that you actually start using it to write your JavaScript, and you open a coffee compiled .js file and you notice the JavaScript looks a little verbose. As a simple example, say you had written a Hello World function called hello.coffee like:
 sayHi = (name) ->
   return "Hello " + name

 console.log sayHi("Ryan")
and you compiled it and opened the .js file and noticed it looked like:
 (function() {
   var sayHi;
   sayHi = function(name) {
     return "Hello " + name;
   };
   console.log(sayHi("Ryan"));
 }).call(this);
As you might suspect, it's just there by default for global namespace cleanliness. You can either manually remove it, or compile your coffeescripts with "-b, --bare". Which would then look like simply:
 var sayHi;
 sayHi = function(name) {
   return "Hello " + name;
 };
 console.log(sayHi("Ryan"))
There is other verbosity such as it always declaring all variables with the var keyword. However, there's no good reason to take them things out since it's actually great to have CoffeeScript do all the tedious things for us that we often skip doing.

That's all for now. Please visit the main CoffeeScript and CoffeeScript Koans sites for more information.

Sunday, October 23, 2011

Approval Tests in PHP

Today, after hearing a talk by Llewellyn Falco, I decided to play with the Approval Tests library. There seems to be plenty of articles on using it with Java, .NET and Ruby but none on PHP, so I figured I'd write about it here.

Approval Tests are about simplifying the testing of complex objects, large strings, et al, where traditional Assert methods might fall short. The Library works with your existing testing framework, be it PHPUnit, Rspec, JUnit, NUnit, what have you. If you've never heard of Approval Tests before please read about them first and come back when you understand the basic concepts. This article focuses on how to use the Approval Tests library in PHP.

To install the Library I downloaded the tarball from the sourceforge download page and then unpacked it into my project's directory. It complained about me not having Zend_PDF so I installed the Zend Framework "minimal" addition. This required adding the Zend Framework's "Library" directory to my PHP include_path. You'll also need PHPUnit installed with PHPUnit_CodeCoverage.

Once installed, getting the approval tests library to work with PHP on my Linux box was tricky because (as of this writing at least) the PHP library seems to have no documentation and is rather incomplete. So I pulled up my sleeves and dug into the source code.

The automatic diff tool and the functionality to display a message for how to move the "received" file to the "approved" file didn't work. The reason is approvals/Approvals.php only had a case for 'html' and 'pdf' and defaulted to the PHPUnitReporter, which itself throws a runtime exception and thus short-cicuits the functionality that takes place in the approve() method.

To remedy this, I added a case for "txt" in the getReporter() method in approvals/Approvals.php. This got it to actually try to call the diff tool but it seems to assume you're using Mac OS X because approvals/reporters/OpenReceivedFileReporter.php does a system() call to the "open" command -- which in OS X will open files as if you double-clicked the file's icon but in Linux it runs 'openvt' -- which in this case will cause an error like "Couldn't get a file descriptor referring to the console." So I edited approvals/reporters/OpenReceivedFileReporter.php and changed:

 system(escapeshellcmd('open') . ' ' . 
    escapeshellarg($receivedFilename));

to:

 system("echo '#!/bin/sh' > /tmp/reporter.command; echo 'diff -u " . 
 escapeshellarg($approvedFilename) . " " .
 escapeshellarg($receivedFilename) .
 "' > /tmp/reporter.command; chmod +x /tmp/reporter.command; 
 /tmp/reporter.command");

That got everything working for me. I'm going to email the author about this and maybe submit a patch to get it working with vimdiff. For now "diff -u" was enough, as I was following the Do The Simplest Thing That Could Possible Work rule.

Now onto the PHP source code.

 <?php
 require_once 'approvals/Approvals.php';

 class Receipt {

   private $items;

   public function __construct() {
     $items = array();
   }

   public function addItem($quantity, $name, $price) {
     $this->items[] = array('name'=>$name, 'quantity'=>$quantity, 'price'=>$price);
   }

   public function __toString() {
     return $this->implode_assoc('=>', '|', $this->items);
   }

   private function implode_assoc($glue, $delimiter, $pieces) {
     $temp = array();

     foreach ($pieces as $k => $v) {
       if (is_array($v)) {
         $v = implode(',', $v);
       }
       $temp[] = "{$k}{$glue}{$v}";

     }

     return implode($delimiter, $temp);
   }
 }


 class ReceiptTest extends PHPUnit_Framework_TestCase {

   /**
    * @test
    **/
   public function Should_create_a_new_receipt() {
     $r = new Receipt();
     $r->addItem(1, 'Candy Bar', 0.50);
     $r->addItem(2, 'Soda', 0.50);
     Approvals::approveString($r);
   }
 }

Running the test the first time creates the file:

    ReceiptTest.Should_create_a_new_receipt.received.txt

in the current directory and fails the test. It's your job to view the generated txt file and decide if it looks right. If it does, rename it to:

    ReceiptTest.Should_create_a_new_receipt.approved.txt

Now when you rerun the test it will diff the current test's output against the approved.txt file's. If they're equivalent, then the test passes, if they're not it doesn't and it's up to you to diff the received and approved files for what the differences are and figure out how to get your test to pass again.

For example, the first time you run the test, it will generate the "received.txt" with the string:

    0=>Candy Bar,1,0.5|1=>Soda,2,0.5

If you rename the file to its approved.txt equivalent, change the quantity of Sodas to 3 and rerun the test, it will fail because it would create another received.txt to diff against the approved.txt but this time the received file will contain:

    0=>Candy Bar,1,0.5|1=>Soda,3,0.5

I hope you approve this article.

Followers