Janico Greifenberg's

Ibis and Baboon

MonkeySpecDoc: Specdoc Output for Ruby's Test::Unit and Shoulda

For my work on RDTN, I did test-driven development (TDD) — although I admit that there is room for improvement in respect to strictness — and also tried out two frameworks for what they call behavior-driven development (BDD): RSpec and Shoulda. While I found it easier to work with Shoulda as a whole, I really like the output formatting options RSpec gives you, especially the specdoc format. So wanted to have this format even when I’m using Shoulda.

For example, let’s assume we have Shoulda tests for a HelloWorld program (you might call this obsessive-compulsive development). The tests look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class TestHelloWorld < Test::Unit::TestCase

  context 'HelloWorld' do

    setup do
      @hello = HelloWorld.new
    end

    should 'say "Hello, World!"' do
      sio = StringIO.new
      @hello.say_hello(sio)
      assert_equal 'Hello, World!', sio.string
    end

  end
end

Let us further assume that we have an implementation that has an error, e.g. the exclamation mark is missing. So, running this with the runner from Test::Unit, we get the following message:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Loaded suite examples/helloworld_test
Started
F
Finished in 0.004184 seconds.

  1) Failure:
test: HelloWorld should say "Hello, World!". (TestHelloWorld)
    [examples/helloworld_test.rb:20:in `__bind_1220472042_539418'
     /var/lib/gems/1.8/gems/Shoulda-1.2.0/lib/shoulda.rb:226:in `call'
     /var/lib/gems/1.8/gems/Shoulda-1.2.0/lib/shoulda.rb:226:in `test: HelloWorld should say "Hello, World!". ']:
<"Hello, World!"> expected but was
<"Hello, World">.

1 tests, 1 assertions, 1 failures, 0 errors

With an equivalent RSpec specification the results look like this

1
2
3
4
5
6
7
8
9
10
11
12
HelloWorld
- should say "Hello, World!" (FAILED - 1)

1)
'HelloWorld should say "Hello, World!"' FAILED
expected: "Hello, World!",
     got: "Hello, World" (using ==)
./examples/helloworld_spec.rb:17:

Finished in 0.006088 seconds

1 example, 1 failure

Implementation

Unlike RSpec which has its own runner logic, Shoulda is a simple add-on to Ruby’s standard Test::Unit suite. So, while MonkeySpecDoc is intended to be used with Shoulda, it also works with plain Test::Unit code. MonkeySpecDoc changes the output logic of the test runner code to separate the information about the test case or the Shoulda context from the name of the individual tests. It also does some tweaking with the output of status messages (OK, FAILED, and ERROR). Technically, what I did was monkey patching the console runner of Test::Unit (hence the name).

Currently, there are two ways to use MonkeySpecDoc:

  1. A run-script called mkspecdoc which takes the file to test on the command line, or
  2. by including specdoc.rb from the MonkeySpecDoc directory in a Rake-task that uses rake/runtest

Returning to the HelloWorld example, MonkeySpecDoc produces the following output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Loaded suite /var/lib/gems/1.8/bin/rake
Started

HelloWorld:
- should say "Hello, World!": FAILED (1)

Finished in 0.004573 seconds.

  1) HelloWorld should say "Hello, World!" FAILED:
    [./helloworld_test.rb:20:in `__bind_1220473238_312818'
     /var/lib/gems/1.8/gems/Shoulda-1.2.0/lib/shoulda.rb:226:in `call'
     /var/lib/gems/1.8/gems/Shoulda-1.2.0/lib/shoulda.rb:226:in `test: HelloWorld should say "Hello, World!". ']:
    <"Hello, World!"> expected but was
<"Hello, World">.

1 tests, 1 assertions, 1 failures, 0 errors

When we fix the error, MonkeySpecDoc says:

1
2
3
4
5
6
7
8
9
Loaded suite /var/lib/gems/1.8/bin/rake
Started

HelloWorld:
- should say "Hello, World!": OK

Finished in 0.000442 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

The code can be found on GitHub including the examples.

Update:

There is now also a Ruby Gem. Run the following if you haven’t added GitHub’s Gem repository to your sources already:

1
gem sources -a http://gems.github.com

Install the gem (Update Dec 18, 2008: project name now in lower case):

1
sudo gem install jgre-monkeyspecdoc