Yes, Even Shell Scripts

December 28, 2004

I write a goodly number of shell scripts because, well, you can do a lot of automation with a low-tech shell script. So I have enough of these buggers now that I can't remember if they all work. In other words, I have more than two scripts. And I feel this twinge of fear every time I tweak one of them.

For example, just the other day I was fiddling with a script that tallies up the number of words in a file. The proper way to do that on Unix is with a wc -w command. The -w part is really, super important because that means "words". And this script was working great, until I tried to make it better. Somewhere in the making-it-better process I changed the -w to a -l. This means to count lines, not words. It turns out that makes a difference.

Darned if I know why I wanted to count lines at that moment, but I suspect it was a debugging aid of some sort. Trouble is, I left that change in there after the debugging frenzy. So for days on end the script happily produced a number. Unfortunately, it was the wrong number. And finally I noticed that this number wasn't increasing at the rate I expected. OK, so a problem like this only takes a few seconds to fix, but that's after I've realized that there is a problem.

Sigh. Yours truly forgot about his friend the self-checking test. So when Jim Weirich blogged about the Ruby Session module, I was all over it. Here's a test case that screams if I inadvertently change -w to -l again:

#!/usr/bin/env ruby

require 'test/unit'
require 'session'

class ScriptTests < Test::Unit::TestCase

  def test_countwords
    bash = Session::Bash.new
    out, err = bash.execute "./countwords.sh tenwords.txt"
    assert_equal 0, bash.exit_status
    assert_equal "", err
    assert_equal "10", out.chomp
  end

  def test_build
    bash = Session::Bash.new
    out, err = bash.execute "./build.sh compile"    
    assert_equal 0, bash.exit_status
    assert_equal "", err
    assert out =~ /BUILD SUCCESSFUL/
  end

end

That second test there checks my build script. See, the build script uses Ant and once in a while I insert my own brand of XML in the Ant build file. And XML parsers haven't figure out my brand yet. So that test checks that the build file gets parsed without error and that it actually runs the Ant target successfully.

So yes, even shell scripts want to be tested. Take my (ahem) word for it. Better yet, scripts can be tested with this handy Ruby module. And if I can test a script after it seems to be working, then why wouldn't I want to eat the dessert first?

Read more posts in the blog archive »