Unit Testing in Rails

Posted on August 8, 2006
Filed Under /dev/null/ | 66 views |

From the guys over at New Bamboo, You never know when the brakes might fail, an excellent comprehensive introduction to unit testing for apps built in Ruby on Rails.

Test-driven development (TDD) is one of those ideas thats been around for, well, forever but you rarely hear much about, even from seasoned developers. I suspect this is because most developers possibly think “It compiled, it ran, it spit out what I expected therefore it’s complete” instead of thinking “It compiled, it ran, it spit out what I expected… what did I miss?” Under that mentality TDD is just an extra layer of cruft that wastes valuable development time creating test cases.

Or, like me not so long ago, developers may not think about test-driven design at all, lacking exposure to it or interest in it. I admit the project I’m currently working on is the first one in which I have comprehensively developed and used test cases against each and every class, writing the tests first and keeping them up-to-date as the classes evolve. And I’m converted, for one simple reason: it’s made me a lazier programmer. I like lazy.

Lazy in the sense that I write some PHP code and rather than switch to a browser and click buttons or enter text on a web page to see if the PHP is syntactically correct (forget about bug-free for the moment, I just want to know that it’ll pass through the interpreter) I switch to the terminal and Up-arrow key:

Sagarmatha:~/Sites/www.newsite.dev/mungo/tests chris$ phpunit MessageRouterTest
PHPUnit 2.3.5 by Sebastian Bergmann.
……

Time: 7.942114

OK (6 tests)

Ta-da. Not only does that class pass all six test cases but I didn’t make any grammatical errors while writing it. If I had mis-typed something it’d let me know right quick:

Sagarmatha:~/Sites/www.newsite.dev/mungo/tests chris$ phpunit MessageRouterTest
PHP Parse error: syntax error, unexpected ‘&’, expecting T_STRING or T_VARIABLE or ‘{’ or ‘$’ in /Users/chris/Sites/www.newsite.dev/mungo/messagingpipeline/class.messagerouter.php on line 77

See? Lazy coding. Using PHPUnit is like getting a twofer and who doesn’t like a good twofer?

The next step in laziness comes when I make a change. Try and remember all code execution paths and points of impact and anticipate the repercussions? Why? Just run the test cases again and see what fails. And then fix it. And run ‘em again. I love it.

Now to some it’ll sound like I’m abdicating responsibility, hoping the machine will catch what I can’t be bothered to. Not so, I say, for your productivity in this case is wholly incumbent upon two things:

  1. The quality of your test cases
  2. The simplicity of your class methods and functions

Which leads me to the next bit of TDD-enabled laziness: simpler functions. The inherent nature of functions is that they tend to grow in linear proportion to the amount of time spent trying to figure out how to make them accomplish a given task, and exponentially in relation to how long the developer has been awake and staring at the computer*.

We’ve all seen it: the classes created at the beginning of the project are svelte, sexy beasts, well-documented, each function accomplishing one task and one task only, optimized to within an inch of their existence. But then later, eventually, at 5am one morning, spasming on a caffeine bender, you look down and realized you’ve created a class with a constructor and one function and called that function doThing(). It takes eight parameters, all of them optional, and somewhere arouund the fourth nested if() statement you realize you’ve completely forgotten what that function was supposed to do.

Then scrap the class and rewrite from scratch.

Writing tests seriously helps mitigate this. Why? Because writing tests is really boring. Dreadully boring. They tend to be chock full of:

$theMsg = $this->msgHandler->getStuffForThing( 3, $theUser );
$this->assertNotNull( $theMsg );
$this->assertNotEquals( ”, $theMsg->get( ‘data’ ) );

I don’t enjoy that at all. But I do enjoy the outcome so I write my tests to be as simple as I can. And by definition simple tests mean simple functions to test against (”simple” meaning small, accomplishing one primary task, and easily digestible by a caffeine-addled brain at 5am). Writing complex functions sucks because writing complex tests sucks. Quid pro bono.

In short: using TDD is like having a spellchecker for your code logic. I like it.

(*Henceforth this shall be known as Cummer’s Law.)

Comments

Leave a Reply