lesscode.org


Fun with Fixtures  

By Ryan Tomayko under Ruby, Rails on 25. September 2005

My favorite part of Agile Web Development with Rails is the section on testing. I’ve found the framework around testing included with Rails to be a wonderful blend of simplicity and power and this chapter in the book is the perfect compliment. It’s based largely on the core Ruby Test::Unit module but adds some important features on top.

One of those features is Fixtures. Fixtures provide a simple, YAML-based file format for storing database state that should be loaded before test runs. The database is wiped and the fixtures are loaded before each individual test executes, providing consistent state for tests. See the section on fixtures in A Guide to Testing the Rails for more information. Here’s an example fixture from said section:

# low & behold!  I am a YAML comment!
david:
 id: 1 
 name: David Heinemeier Hansson 
 birthday: 1979-10-15 
 profession: Systems development

steve:
 id: 2
 name: Steve Ross Kellock
 birthday: 1974-09-27
 profession: guy with keyboard

The cool thing is that the top level fixture names (in this case, “david” and “steve”) become instance variables in your test case, allowing you to access fixture data in a very intuitively way from tests. The result of this is test code that reads like a story and is often almost humorous.

The book includes a sidebar with a little David head lamenting the importance of “Picking Good Fixture Names”:

Just like the names of variables in general, you want to keep the names of fixtures as self-explanatory as possible. This increases the readability of the tests when you’re asserting that @valid_order_for_fred is indeed Fred’s valid order. It also makes it a lot easier to remember which fixture you’re supposed to test against without having to look up p1 or order4. The more fixtures you get, the more important it is to pick good fixture names. So, starting early keeps you happy later.

But what to do with fixtures that can’t easily get a self-explanatory name like @valid_order_for_fred? Pick natural names that you have an easier time associating to a role. For example, instead of using order1, use christmas_order. Instead of customer1, use fred. Once you get into the habit of natural names, you’ll soon be weaving a nice little story about how fred is paying for his christmas_order with his invalid_credit_card first, then paying his valid_credit_card, and finally choosing to ship it all off to aunt_mary.

Association-based stories are key to remembering large worlds of fixtures with ease.

Taking this advice, I started in on testing a part of an application I’m working on now. The result is worth posting in it’s entirety (hint: it gets interesting towards the middle):

require File.dirname(__FILE__) + ‘/../test_helper’

class EnrolleeTest < Test::Unit::TestCase
  fixtures :coverage_types, :plans, :coverages, :plan_levels, :ppo_options,
           :elections, :enrollees, :enrollments, :tpas, :tpas_users, :users,
           :roles

  def setup
    @joe = Enrollee.find(@joe_the_policy_holder.id)
    @rita = Enrollee.find(@rita_the_spouse.id)
    @billy = Enrollee.find(@billy_the_dependent.id)
    @alice = Enrollee.find(@alice_the_dependent.id)
    @the_family = [@joe, @rita, @billy, @alice]
    @enrollment = Enrollment.find(@test_enrollment.id)
  end

  
  def test_common_attrs
    @the_family.each do |enrollee|
      assert_kind_of Enrollee, enrollee
      assert_equal @enrollment.id, enrollee.enrollment_id
      assert_equal @enrollment, enrollee.enrollment
      assert_equal @joe.id, enrollee.policy_holder_id
      assert_equal @joe, enrollee.policy_holder
    end
  end

  def test_get_gender
    assert_equal :male, @joe.gender, "Joe is a male"
    assert @joe.male?, "Joe is a male"
    assert_equal :female, @rita.gender, "Rita is a female"
    assert @rita.female?, "Rita is a female"
    assert !@billy.female?, "Billy is not a female"
    assert !@alice.male?, "Sally is not a male"
  end

  def test_set_gender
    [:female, ‘F’, 2].each do |tok|
      @joe.gender = tok
      assert_equal :female, @joe.gender, "Joe is now a female"
      assert @joe.save, "Joe could not be saved after sex change"
      @joe.reload
      assert_equal :female, @joe.gender, "Joe is now a female"
    end

    [:male, ‘M’, 1].each do |tok|
      @rita.gender = tok
      assert_equal :male, @rita.gender, "Rita is now a male"
      assert @rita.save, "Rita could not be saved after sex change"
      @rita.reload
      assert_equal :male, @rita.gender, "Rita is now a male"
    end

    # make sure that we can set it to nil
    @joe.gender = nil
    assert_equal nil, @joe.gender
    assert @joe.save(false), "Joe couldn’t be saved after neutering…"
    @joe.reload
    assert_equal nil, @joe.gender

    @joe.gender = ‘’
    assert_equal nil, @joe.gender
    assert @joe.save(false), "Joe couldn’t be saved after neutering…"
    @joe.reload
    assert_equal nil, @joe.gender
  end

  def test_marital_status
    assert_equal :married, @joe.marital_status
    assert @joe.married?, "Joe is married"
    assert !@joe.single?, "Joe is not single"
    assert @billy.single?, "Billy is single"
    assert !@billy.married?, "Billy is not married"
    assert_equal :single, @billy.marital_status
  end

  def test_set_marital_status
    [:married, 2].each do |tok|
      @billy.marital_status = tok
      assert_equal :married, @billy.marital_status
      assert @billy.save, "Billy could not be saved after he got married"
      @billy.reload
      assert_equal :married, @billy.marital_status
    end

    [:single, 1].each do |tok|
      @rita.marital_status = tok
      assert_equal :single, @rita.marital_status
      assert @rita.save, "Rita could not be saved after her divorce"
      @rita.reload
      assert_equal :single, @rita.marital_status
    end
  end
end

:)

2 Responses to “Fun with Fixtures”

  1. Rimantas:

    Is this the interesting part:

    assert !@alice.male?, “Sally is not a male”

    ;)

    comment at 25. September 2005

  2. Ryan Tomayko:

    Nice catch!

    comment at 25. September 2005

Leave a Reply

Note: None of this information is required but leaving a Name and URL is much appreciated. You can also register to have this stuff remembered.

Your comment can be previewed here.


Markdown: use the force, Luke.