#158 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.
- Download:
- source code
- mp4
- m4v
- webm
- ogv
Thank you, Ryan. You read my mind. I just wanted to read about Factory Girl. :-)
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.
To answer my own question, you can use sprintf to get leading zeros.
http://pastie.org/452449
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
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.
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.
Thanks Ryan. Good post.
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.
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
@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.
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.
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.
@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?
which textmate theme are you using?
@ Greg
> Are there any factories for
> regular objects?
What would a "regular object factory" provide that factory_girl or machinist does not?
@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?
Great screencast. Thanks so much!
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!
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.
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.
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!
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.
Any body have any 2 cents on when is a good time to use Factory.stub? Just curious. Thx.
This one was a little difficult to follow because you never tell us about when something is magic. For instance when you use the factory to create a user name an password is it magic that it uses your username and password or did it ignore it and create them using the "foo" name? When you test for the name and pass you provided it passes so I THINK it's using magic but I'm not sure. Especially since you specifically use the user.password for the verification thing.
It would seem to make more sense (if it isn't using magic) to do a: f.username = user.username || "Foo". But again it might already be doing that for us.
So, in the future, PLEASE point out the magic bits so it makes more sense. I've noticed you breeze over them a lot in all of your casts. Hopefully you accept this as constructive criticism.
Anyway, thanks a ton for the 'casts. They have been a great help in learning ruby and rails.
ryan, please put a f*ckin captcha to the comments form.
Is there a way to reset the FactoryGirl sequence? It seems to persist between cucumber sessions which breaks my tests!
Nevermind that last comment. It wasn't persisting between cucumber sessions, but rather across scenarios when I thought it was reset for each. Read more about transactions and before/after hooks for info.
According to the FactoryGirl documentation a lot of the syntax in this episode is outdated. Here is some of it rewritten following this.