Generating a SPECDOC from tests in Rails

Thursday, March 29

So I’ve been using RSpec for a while, but sometimes its integration with the Rails framework frustrates me. On the last project I started, I decided not to make waves and just use Test::Unit, which is the Rails default. It’s just easier that way.

Still, there are some things I like about RSpec. One of these things is the ‘this should do that’ syntax, which feels more natural to me. So, ever since I’ve been back on the Test::Unit bandwagon, I’ve been writing by tests in a test_should_do_something_amazing style. (I’ve noticed that a lot of Rails developers are using this style as of late, and even the test stubs that the Rails generators spit out are using this style as well).

Another thing I like about RSpec is its ability to produce a specification document based on your tests, which it calls a SPECDOC. Inspired by the recently added rake task notes, (which enumerates all the commented fixmes, todos, etc. in your app), I figured a SPECDOC enumerator wouldn’t be too hard to add. Here’s a stab at a rake task that does the job. This will rip through your tests, look for specification-style tests, and generate a SPECDOC in the docs directory.

desc "Generate specdoc-style documentation from tests" 
namespace :doc do
  task :specs do
    puts 'Started'

    timer, count = Time.now, 0

    File.open(RAILS_ROOT + '/doc/SPECDOC', 'w') do |file|
      Dir.glob('test/**/*_test.rb').each do |test|
        test =~ /.*\/([^\/].*)_test.rb$/
        file.puts "#{$1.gsub('_', ' ').capitalize} should:" if $1
        File.read(test).map {|line| /test_should_(.*)$/.match line }.compact.each do |spec|
          file.puts " * #{spec[1].gsub('_', ' ')}" 
          sleep 0.001; print '.'; $stdout.flush; count += 1
        end
        file.puts
      end
    end

    puts "\nFinished in #{Time.now - timer} seconds.\n" 
    puts "#{count} specifications documented" 
  end
end

Place this snippet in a file called specdoc.rake and throw it in lib/tasks and you’ll be good to go. Run it using rake doc:specs as follows:

$ rake doc:specs
Started
..............................................................................
..............................................................................
......................................
Finished in 2.308674 seconds.

194 specifications documented

The dots and chatter are all just eye-candy, but I thought I’d add them in for good measure. I’m thinking of removing it, though, just to cut down on the code. The SPECDOC will be output to the doc directory.

$ cat doc/SPECDOC 
Memberships controller should:
 * require login for protected actions
 * not require login for public actions
 * create and send invitations

Users controller should:
 * create user
 * require unique login on create
 * require password on create
 * require password confirmation on create
 * require email on create
 * require valid email on create
 * require authentication
 * accept authentication
 * get new user
 * show user
 * update user
...

Note that this will only document tests that begin with the aforementioned test_should_ prefix. Test cases that don’t use this convention are ignored, though you could tweak the regex to include them.

My regex-fu may be lacking, so if anyone has any suggestions for how I could make this better, don’t hesitate to pipe up.