Tuesday, March 11, 2008

An Editor Independent Unittest Executor

Since I got test infected I'm somehow unable to write a single line of untested code without feeling uneasy. When I just want to write a tiny script containing a few lines of code in whatever text editor is installed in a system, it seems to be a daunting task to set up a programming environment that allows you to execute unit tests with a single click. But this single click is what makes writing unit tests unobtrusive enough to keep doing it.

So I'm quite fond of using a simple script to execute my script's unit tests whenever I save it. This concept is not new, and certainly not an original idea in itself, but the simplicity of an editor independent unit test executor in 10 lines of code has a certain appeal for me:

#!/bin/bash
stat_command="stat -c '%Y'"
file_name=$1
last_modification=""
while true; do
current_modification=$( $stat_command $file_name )
if [ "$current_modification" != "$last_modification" ]; then
clear
$file_name --test
last_modification=$current_modification
fi
sleep 1
done

This script stats the script file until it detects a change. Whenever a change is detected, the script is called with --test, which is my personal way to tell a script that it should just execute it's unit tests and exit. See my blog post about integrating unit tests in Ruby scripts to learn how this can be done in Ruby. A very similar approach is possible for Python:

#!/usr/bin/python
import unittest
import sys

if sys.argv.count("--test") > 0:
sys.argv.remove("--test")
unittest.main()


Now I can simply call the test bash script, giving it the script under test as parameter:

./run_tests.sh ./script_under_test.py


The beauty lies in the simplicity of the solution: Even when I remote edit a script on some server with vi, I can simply launch a new console and execute run_tests.sh, watching the test results whenever I type ":w".

Update: The "sleep 1" really helps to keep I/O load down. Thanks to Philip for pointing this out. And yet another nice example of how hard it is to write 10 lines of bugfree code without a test.