#158
Apr 20, 2009

Factories not Fixtures

Fixtures are external dependencies which can make tests brittle and difficult to read. In this episode I show a better alternative using factories to generate the needed records.
Tags: testing tools
Download (22.9 MB, 12:36)
alternative download for iPod & Apple TV (15.7 MB, 12:36)

Resources

sudo rake gems:install RAILS_ENV=test
# config/environments/test.rb
config.gem "thoughtbot-factory_girl", :lib => "factory_girl", :source => "http://gems.github.com"

# spec_helper.rb
require File.dirname(__FILE__) + "/factories"

# spec/factories.rb
Factory.define :user do |f|
  f.sequence(:username) { |n| "foo#{n}" }
  f.password "foobar"
  f.password_confirmation { |u| u.password }
  f.sequence(:email) { |n| "foo#{n}@example.com" }
end

Factory.define :article do |f|
  f.name "Foo"
  f.association :user
end

# user_spec.rb
describe User do
  it "should authenticate with matching username and password" do
    user = Factory(:user, :username => 'frank', :password => 'secret')
    User.authenticate('frank', 'secret').should == user
  end
  
  it "should not authenticate with incorrect password" do
    user = Factory(:user, :username => 'frank', :password => 'secret')
    User.authenticate('frank', 'incorrect').should be_nil
  end
end

RSS Feed for Episode Comments 37 comments

1. Roman Apr 20, 2009 at 01:30

Thank you, Ryan. You read my mind. I just wanted to read about Factory Girl. :-)


2. Jeffrey Lee Apr 20, 2009 at 08:46

How would you go about sequencing a specific number length? For example, if I need a 7 digit number, how could I specify that the number must have leading zeros?

I'm currently doing:

http://pastie.org/452428

while this works, it certainly isn't ideal. I'd rather have an option such as :length => 7 that would produce: 0000001, 0000002, etc.


3. Jeffrey Lee Apr 20, 2009 at 08:55

To answer my own question, you can use sprintf to get leading zeros.

http://pastie.org/452449


4. Dan Croak Apr 20, 2009 at 10:16

Excellent screencast as usual.

You can use Machinist's or Object Daddy's syntax from within Factory Girl if you like either of those styles better:

http://giantrobots.thoughtbot.com/2009/2/17/factory_girl-1-2-adding-excitement-to-stale-factories

http://dev.thoughtbot.com/factory_girl/classes/Factory/Syntax.html


5. jnstq Apr 20, 2009 at 14:31

Thanks for great episode! I have a command in my TextMate bundle for generating factory code for a model in the database. It will build attributes with default values from database if present. It's up on github, http://github.com/jnstq/rubyeqp/tree/master. A real time saver.


6. andy Apr 20, 2009 at 16:05

Great screencast. But I am left wondering - why would you want to do this? It looks to me like factory girl is more complicated than using fixtures.


7. George Apr 20, 2009 at 20:38

Thanks Ryan. Good post.


8. Jeff Dean Apr 20, 2009 at 21:08

Thanks! I've been using object mother patterns on all my apps for several months now, and although they are slower, the increase in readability and maintenance more than makes up for it.

My favorite object mother gem by far is Fixjour - http://github.com/nakajima/fixjour/tree/master - which I now use pretty much exclusively.


9. Josh Apr 21, 2009 at 07:18

Great screencast Ryan! You have an uncanny way of touching on topics that are on my mind.. I hope you can't read all of my thoughts! ;D


10. Ryan Bates Apr 21, 2009 at 08:10

@MadDog and andy, this may seem like needless work up front, but it will result in more maintainable and easier to read tests. Just like any refactoring, you may not see the benefits immediately.

Fixtures can quickly get out of hand as the test suite grows. Once you have 20 different records in a fixtures file of varying attributes trying to get each edge case it becomes difficult to maintain. All of the tests are sharing this same data and no single test really uses all of the records at once.

In short, if you build tests which aren't heavily dependent on external data (fixtures) then they will be much more maintainable and easier to read because everything that is important to that test is in one place.


11. Vishal Apr 22, 2009 at 01:42

Thanks Ryan for this.
But i think a more better option for test would be machinist.
it is very easy to use.
I have used it in my project.


12. Greg Apr 23, 2009 at 09:27

Are there any factories for regular objects? FactoryGirl, Mechanize, ObjectDaddy and Fixjour are great frameworks for ActiveRecord. But sometimes people write code that doesn't use ActiveRecord.


13. Tim Matheson Apr 23, 2009 at 20:51

@Ryan Bates
When defining a new factory, couldn't you just do this to achieve a random value for fields?

Factory define :user do |f|
  f.full_name "Tim Matheson"
  f.email "me@timmatheson#{rand(9999)}.com"
end

Wouldn't that do the same thing? Or is it best to stick with the block as it is more reliable?


14. Luka Apr 25, 2009 at 17:08

which textmate theme are you using?


15. James B. Byrne Apr 26, 2009 at 18:18

@ Greg
> Are there any factories for
> regular objects?

What would a "regular object factory" provide that factory_girl or machinist does not?


16. andy Apr 27, 2009 at 08:39

@ryan - thanks for your response!

does anyone have any guidelines on when to use factories vs mocks?

i am planning to use factories for unit tests, and mocks for controllers, and 'composite objects'. does that sound like a good strategy?


17. Vadim May 26, 2009 at 02:49

Thank you for the excellent article, for you became on one subscriber anymore


18. Justin Charles Beck Jun 12, 2009 at 13:06

Great screencast. Thanks so much!


19. Sig Jun 16, 2009 at 00:56

Hello Ryan,
great screencast as always.
I'm trying to update the user factory you have created on this episode in order to handle password_hash and password_salt (when the user inputs his/her password).

Any ideas about how to deal with this issue?

Thanks and have a good 1!


20. David Johnson Jun 23, 2009 at 22:34

Anyone have any links to using stubs from Factory_Girl, I can't seem to get it to work. I have inherited code where the models have a dependency of A belongs to B which belongs to A prime (polymorphic).

Think sales person belongs to a region which has a manager (which is just a polymorphic version of the [sales]Person table). With fixtures it "auto" built the tables so I didn't need to worry about initial validations, I can't figure out how to do that with Factories.


21. Justin Jun 25, 2009 at 15:14

How do you resolve circular associations in factories? I have an account that contains a collection of users one of which is the account owner:

Factory.define :account do |f|
  f.state "pending"
  f.association :owner, :factory => :user
end

Factory.define :user do |f|
  f.sequence(:first_name) { |n| "factory_user#{n}" }
  f.last_name "Smith"
  f.password "test"
  f.password_confirmation { |u| u.password }
  f.email { |a| "#{a.first_name}@example.com".downcase }
  f.eula "1"
  f.association :account
end

It loops in to infinity and crashes the tests... Is there an obvious way around this? Thanks for your help and thanks for a great set of casts.


22. Anime Jul 14, 2009 at 04:35

Thanks Ryan. Good job.


23. çizgi film oyunları Aug 23, 2009 at 14:14

Good article, thanks to the author to share


24. filmy online Aug 29, 2009 at 06:44

Very nice screencast. Thank you so much!


25. Evelina Sep 13, 2009 at 12:56

It was pleasant to visit your site.


26. Marinya Sep 15, 2009 at 12:31

Пишите по-русски! Ничё, блин, непонятно!


27. G-man Sep 21, 2009 at 21:19

Since this project seems to use restful_authentication, and the newer episodes have moved to authlogic, I have been refactoring this tutorial to work with authlogic -- a good way to learn!


28. IdahoEv Nov 04, 2009 at 22:19

I'm pretty fed up with Factories, and specifically Factory Girl.

Yes, they can make for easier-to-read tests. But they're a lot slower than fixtures, and Factory Girl is so poorly documented that they waste huge amounts of my coding time. In some cases, it's also very inflexible, and things that would be easy to do with fixtures are very hard to do with Factory Girl.

Example: try finding the documentation for how to create a simple has_many relationship. Say, Topic has_many Posts, such that your default factory for Topic creates one with three simple posts.


29. bmw gt1 Nov 30, 2009 at 08:05

Just continue writing this kind of post. I will be your loyal reader. Thanks


30. uggs Jan 05, 2010 at 18:14

I am apreciating it very much! Looking forward to another great article


31. nike outlet Jan 12, 2010 at 16:32

Great screencast.
Thank you for sharing.


32. kamagra Jan 19, 2010 at 11:41

thanks


33. m65 jacket Jan 19, 2010 at 11:43

very nice share


34. uggs Jan 24, 2010 at 17:38

Fascinating stuff. As a follow-up, may I suggest that you contact


35. ramiro Jan 24, 2010 at 19:00

Nice post,nice sharing.Thanks!


36. nike shocks for men Jan 25, 2010 at 01:21

when nike 7.0 shoes came out of retirement for the second time, there has already been 13 series of lebron james basketball shoes.Welcome to http://www.nikedirect.net/


37. NFL PRO BOWL jerseys Jan 25, 2010 at 01:23

Knowledge and know-how to spearhead transition efforts over nhl alternate jersey the next few months. Welcome to http://jersey-online.org/

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player
Give Back to Open Source