Factory Girl for the win
In my last post I talked about testing and in this post I’d like to continue that theme.
As a classicist I like to create actual objects when I test. I find it makes my tests easier to write, shorter and more readable, but creating valid objects can be a messy business.
1 class ProductControllerTest < ActionController::TestCase 2 def test_should_show_price 3 product = Product.create!( 4 :name => 'Dragons', 5 :brand => '5/10', 6 :sku => 'ABC', 7 :ean => '510DRAG', 8 :price => '75.00') 9 10 get :show, :sku => 'ABC' 11 assert_select '.price', '12.50' 12 end 13 end
In the test above we only care about the price and the sku of the product, having all these other attributes is messy but a valid Product requires all of them. It is necessary to create a valid object but we don’t need to do it in this clunky, verbose way. Enter the Object Mother
Essentially the Object Mother is just a factory for creating test objects that allows use to concentrate on the attributes we care about in the test.
1 class Mother 2 def self.create_product(attributes = {}) 3 Product.create!({ 4 :name => 'Product', 5 :brand => 'Brand Name', 6 :sku => 'SKU', 7 :ean => 'EAN', 8 :price => 10.00}.merge(attributes)) 9 end 10 end 11 12 class ProductControllerTest < ActionController::TestCase 13 def test_should_show_price 14 product = Mother.create_product({:price => 75.00, :sku => 'ABC'}) 15 16 get :show, :sku => 'ABC' 17 assert_select '.price', '75.00' 18 end 19 end
Mission accomplished, the test is much more succinct because the data we created in the test is relevant to that test. But that’s not the end of the story. The example above is very simple, what if in our test we wanted 2 or more products and a valid product has unique EANs and SKUs.
Option 1) Pass the unique parts into the Mother – but that’ll pollute the test with irrelevant data again.
Option 2) Modify the Mother to dynamically create values for all the unique attributes
Option 3) Use something that already does Option 2 and a whole load of other things
Enter Factory Girl
1 #tests/factories.rb 2 Factory.sequence :sku do |n| 3 "SKU#{n}" 4 end 5 Factory.sequence :ean do |n| 6 "EAN#{n}" 7 end 8 Factory.define :product do |p| 9 p.name "Product" 10 p.brand "Brand" 11 p.price 12.50 12 p.sku{ Factory.next :sku } #dynamically set the sku 13 p.ean{ Factory.next :ean } #dynamically set the ean 14 end 15 16 #controller test 17 class ProductControllerTest < ActionController::TestCase 18 def test_should_show_price 19 product = Factory(:product, :price => 75.00, :sku => 'ABC') 20 21 get :show, :sku => 'ABC' 22 assert_select '.price', '75.00' 23 end 24 end
Not only does Factory Girl do the job of make the test readable and relevant, it also handles sequences (like the ean and sku values), associates and more.
