Fixtures in Ruby Unit Tests

I’m writing some Ruby scripts that sort and store lots of small files. After a day or two of hacking I had the basic code working, ran through a few thousand files, and a malformed file blew up the sorter. That was OK, the sorter was intentionally naive and lacking in error handling; I wanted it to hack it together and try out a few approaches before committing to serious development.

Now it’s time to treat it as real code, which means getting it under test. Tests are especially useful for ensuring tricky edge cases like malformed input don’t quietly break during other changes. When I started writing unit tests for the sorter I wanted fixtures similar to the Rails fixtures so I could write:

require File.dirname(FILE) + ‘/../test_helper’ require ‘message’

class MessageTest < Test::Unit::TestCase fixtures :message

def test_initialization f = message(:good) # assert the object was parsed correctly, etc. end end

The basic usage is fixtures :type to load the data and create the named function (in this case, message) for fetching data. First, I wrote some YAML fixtures for my data and put them in test/fixtures/message.yaml. You can see the :good key in use here:

good: |
  [the body of a good message here]
missing_header: |
  [more text]
bad_checksum: |
  [more text]

Because I’m parsing textfiles my fixtures look pretty simple, but the YAML library for Ruby makes serialization of any very easy.

So here’s the code I put in test/test_helper.rb to set this up. I really like the [list].flatten.each idiom so the call from the unit test class can cleanly pass one or more types, eg: fixtures :message and fixtures :message, :user.

` class Test::Unit::TestCase @@fixtures = {} def self.fixtures list [list].flatten.each do fixture self.class_eval do # add a method name for this fixture type define_method(fixture) do item # load and cache the YAML @@fixtures[fixture]   = YAML::load_file(“test/fixtures/#{fixture.to_s}.yaml”) @@fixtures[fixture][item.to_s] end end end end end `{lang=”ruby”}

(Note to Rails coders: I deliberately left out the pluralization, I like having the File class in file.rb with the fixtures in text/fixtures/file.rb and the database table named file and so on. I don’t mind Rails’ convention of pluralizing to “files” in the latter two examples, but I’d rather have a single identifier.)

Last night at the Rudolph Hering Society meeting Ian Bicking and Atul Varma explained mocking to me. I wrote a (very) little Ruby this morning to do mocking and I’ll post that in a day or two when I’m sure I didn’t do it too badly.