October 19, 2007

About a year ago, Mike and I were pair programming and I watched as he added the following to the bottom of the unit test file for our project’s User model:

protected

def create(options={})
  User.create({
    :username => "jdoe",
    :first_name => "John",
    :last_name => "Doe
  }.merge(options))
end

and I remember thinking, “Slick! We can eliminate fixtures entirely, once we move that into test helper with a name like create_user so that we can use it from the other model’s unit tests!” Then we’re able to do stuff like:

def test_should_return_users_ordered_by_username
  betty = create_user(:username => 'betty', :first_name => 'Betty')
  ann = create_user(:username => 'ann', :first_name => 'Ann')
  assert_equal [ann, betty], User.find_ordered
end

Today, I found the blog of Dan Manges and had one of those forehead smacking moments when I saw code that would resemble this from the previous example:

module Factory
  def self.create_user(attributes = {})
    default_attributes = {
      :username => "jdoe",
      :first_name => "John",
      :last_name => "Doe
    }
    User.create! default_attributes.merge(attributes)
  end
end

Put that module in its own file (like test/factory.rb) and then requiring it from the test_helper, clearing all of those method definitions out of the test helper and use it like:

def test_should_return_users_ordered_by_username
  betty = Factory.create_user(:username => 'betty', :first_name => 'Betty')
  ann = Factory.create_user(:username => 'ann', :first_name => 'Ann')
  assert_equal [ann, betty], User.find_ordered
end

And though that’s pretty cool and allows you to eliminate fixtures entirely, that’s not the coolest part.

The Coolest Part

About a week ago, Dan and a couple of other guys released the DeepTest gem, which “essentially… throws all the tests onto a tuple space. workers take the tests and write results back. simple.” It parallelizes your tests! And you can set the number of processes, so you can work those multicore machines they way they were meant to be worked (tests on our fat CI box at work just got a whole lot faster…)! As long as you’re not using fixtures because, “if you’re using fixtures you won’t be able to take advantage of deeptest to run your tests twice as fast. data from fixtures is loaded outside the transaction. therefore, loading fixtures in one process will mess up tests running in another.”

Great work, guys!