2 techniques for rails testing without fixtures

November 13, 2007

After it taking me 6 months to be converted to TDD, the last 6 months I have been improving my skills in the TDD arena. After developing some fairly complex systems I was finding fixtures to be far too brittle and was relying on the ar_fixtures plugin to generate fixture data.

The thing is, this isn’t right. You shouldn’t be generating the fixtures from data created by untested code!

To get straight to the point, the following 2 techniques are great for fixture-less testing.

1. Have a phat test_helper.rb

Use transactional fixtures.

In your test_helper.rb you should be extracting out all the object creation code so you can use it over multiple tests.

eg.

def create_property(options={})
    Property.create!(
    {
      :title => 'Flat in Skye',
      :address_line_1 => 'Flat 2/1',
      :address_line_2 => '75 Old Dumbarton Road',
      :city => 'Glasgow',
      :postcode => 'G3 8RG',
      :short_description => 'Great flat in Yorkhill',
      :full_description => 'This is in a great location and has top views of the university',
      :min_people => 1,
      :max_people => 10,
      :tag_list => "Dishwasher\nTV",
      :landlord_id => 1,
      :is_live => true
    }.merge(options)
    )
  end

This is great because in my tests I can run something like.

def test_special_dom
    p = create_property(:is_live => false)
    special_dom = p.special_dom('new')
    assert_not_nil(special_dom)
    assert_equal("property_#{p.id}_new", special_dom)
  end

This means that the setup and tear down for this test is done in the test method meaning I know exactly what I am working with. If you look at line 2 you will see you I can override the default options in the test method by passing a hash of new options. This is the beauty of the merge method in test_helper.rb

2. Use the setup method in both unit and functional tests.

This is not only great for logging in users, but it also is great for running the data creation methods before you run the test and make all your test methods a little more DRY.

eg.

def setup
    @controller = LandlordAjaxController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
    @property = create_property
    login_landlord
  end

Now all my tests in this functional test can use the @property object.

eg.

def test_back_calendar
    xhr :post, :back_calendar, :property_id => @property.id, :month_date => Date.today.to_s(:db)
    assert_rjs :replace_html, 'months'
  end

Thats all for now. If you have any more tips for fixtureless testing please add them to the comments.