This is for those of you who have moved past fixtures in your rails tests, but haven’t yet discovered factories. If you are creating test objects manually in your tests, this is for you. If you are creating test objects using a bit more abstraction, you may move on. If you are still using fixtures, there is no hope for you. Become a forest ranger.
Go on.
Whoever is left, you are in a transition period. You are probably writing tests that look like this:
should "return only buttered biscuits" do
yummy = Biscuit.create!(:buttered => true)
gross = Biscuit.create!(:buttered => false)
assert_equal [yummy], Biscuit.buttered
end
Not bad, but now you want to add a required attribute to Biscuit. Since you are Agile Coder Extraordinaire, you write the test first:
# You're using Shoulda here, hence the brevity:
should_require_attributes :flavor
Failed! Good. You now make it green by adding the attribute and validation. That test is passing, but wait… now you’re seeing red again! Oh no!
1) Error: test: Biscuit should return only buttered biscuits. (BiscuitTest): ActiveRecord::RecordInvalid: Validation failed: Flavor can't be blank
Looks like you have to go add the :flavor attribute to the create calls in that first test. Might not seem like a big deal, but imagine if you had a much larger test suite and creates like that were sprinkled throughout. You’d be fixing failing tests all day that have nothing to do with the behavior you’re actually changing. Yuck.
You need to abstract the creation of your test objects. That’s where factories come in. A factory is basically an object with methods for creating other objects. This lets you set up a baseline set of default attributes in one place instead of all over the place.
This could be as simple as a helper method in test_helper.rb. Just remember to allow overriding your default attributes:
# In test_helper.rb
def create_biscuit(attributes = {})
Biscuit.create!({
:flavor => "Homestyle"
}.merge(attributes))
end
Again, you could do it this way, but since you are smart, you decide not to reinvent the wheel, and use factory_girl instead:
# In factories.rb
Factory(:biscuit) do |b|
b.flavor "Homestyle"
end
Now you can write your test like this:
should "return only buttered biscuits" do
yummy = Factory(:biscuit, :buttered => true)
gross = Factory(:biscuit, :buttered => false)
assert_equal [yummy], Biscuit.buttered
end
and never touch it again!
unless you change the behavior of buttered of course…
Other Posts That Might Interest You




