2 techniques for rails testing without fixtures
November 13th, 2007 by pyrat
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.
November 13th, 2007 at 11:08 pm
Good to see people that have recently converted to TDD are starting to realize the errors of the fixture ways. However, what you are doing here is just creating fixtures in Ruby code (rather than the YAML fixtures). I have noticed far superior “testing” can be achieved by using RSpec and adopting the BDD ways instead of regression-heavy TDD way. I see a big difference between the two (TDD and BDD).
Many old hat extreme prgrammers (XPers) find the BDD way unintuitive, but I find the BDD a more comprehensive way to “specify” your software. The mindset shift you need to make coming from regression-heavy TDD mindset is that you need to start thinking about testing software contracts instead of asserting output data values assuming certain input data.
If you start a new Rails (or any Ruby) project, I would strong suggest you use RSpec instead of Test::Unit and take full advantage of the builtin mocking and stubbing, so fixtures (even ones created in Ruby code) will be a thing of the past.
If you find the RSpec way a little too much you can also utilize mocha to mocking objects decently. The only major benefit you miss by going half assed with Test::Unit+mocha is that you don’t have the syntactic sugar, which surprisingly provides clarity while thinking about your software specifications.
November 14th, 2007 at 10:11 am
Thanks for the comment SP.
I hope to use Rspec for the next project that I work on. I went to the Rspec tutorial at railsconf europe and was intrigued.
I’ll let you know how it goes.