Fixtures in Ruby Unit Tests «
»


Code: , , , ,
3 comments

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.new 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

(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.


Comments

  1. Like a n00b I copied this code to see what would happen and got the following error message:

    ArgumentError: assertion message must be String or Proc, but NilClass was given.

    This is because Test::Unit::TestCase already defines a message function, so the code overloads this internal function. Changing message to messages solved this for me.

  2. Thanks for the note. This was directly copied out of my codebase, so Test::Unit must’ve changed in the last few years. C’est la programming vie.

Leave a Reply

Your email address will not be published.