How to use factory_girl with rspec?
If you were expecting me to tell you, sorry, because I have no idea. So it was either write a post about it, in the hopes that someone more knowledgeable than I am will tell me why I’m being ignorant, or write “I hate testing” as many times as I can before jabbing a pencil… anyway, I chose the former.
I’m trying not to let my frustrations overtake this post, so I better get to the point.
Everything is setup fine. Gems are installed, libraries are required, and so on. I’ve added a spec/factories directory, where I’m keeping all of my factories. A factory might look something like this, for example:
1 2 3 4 | Factory.define(:user) do |f| f.name "Ryan Heath" f.email "ryan@rpheath.com" end |
Before your tests are run, all of the factories are loaded into the test database for use within your tests. Here’s how to make use of the factories in your tests (using rspec as the guinea pig):
1 2 3 4 5 6 7 8 9 10 11 12 | describe Whatever do before(:each) do @user = Factory.build(:user) # returns an unsaved object @user = Factory.create(:user) # returns a saved object @user = Factory(:user) # shortcut for Factory.create() end it "should be a dumb test to show usage" do @user.name.should == "Ryan Heath" @user.email.should == "ryan@rpheath.com" end end |
So when you want an unsaved object, no problem, just use Factory.build() and you get an object that has a name of “Ryan Heath” and an email of “ryan@rpheath.com”. If you want that same object, but want it to be a saved instance, use either Factory.create() or Factory().
So far things seem to make sense. Nice organization for your factories and a nice easy way to generate/save them. Except what about validations? If all of the factories in spec/factories/*.rb are loaded into the database before my tests are run, when I do Factory(:user) and it tries to create another user with the same email that’s already in the database, what do you think happens? You guessed it, validations fail.
Now to me, it seems like Factory(:user) should first return the user from the DB if there is one, then create it if not. But that could be due to my sheer ignorance of how to really make use of factory_girl.
The reason why I threw rspec in the mix is because it sometimes seems like rspec isn’t clearing the database when it should be. For instance, between unit and functional tests (er, specs).
So, internet, what am I missing, here? Questions…
- Am I correct in using
before(:each) { @user = Factory(:user) }? - If so, why are my specs blowing up because of failed validations? (yes, I have
config.use_transactional_fixturesset to true) - How can I get the user out of the database? Is there a
Factory.load(:user)or the like? (that would pretty much solve my problems) - Do I really have to call a “clear_database!” method in a
before(:all)block at the top of each spec? - Where can I see excellent factory_girl/rspec usage on a rather large project?
Don’t let me down. The winner gets the satisfaction of knowing that he or she probably saved a poor, defenseless MBP screen.

Gabe Hollombe Wednesday, 12 Aug, 2009 Posted at 01:02AM
I don’t use RSpec so I can’t help with 1 & 2 (I prefer Shoulda and plain old Test::Unit). But, let me see if I can help with question #3.
My understanding is that you don’t ever use Factory Girl to get data out of the database; you only use it to build instances of models that you may or may not save to the database, as in Factory.create(:user) vs Factory.build(:user). If you want to pull a user out of the database, why not just use User.find()?
The way I typically use factories is that I’ll set up all the instances of models that I care about in my context’s setup block, then work with those instances in my tests. So, for example, I might do:
So, because I’ve only created instances of the models that my test cares about in the setup block, I’ve already got handles to them for my tests, and I don’t need to ask the db to get me the Gabe or Ryan users, unless I wanted to (like the example above does using User.find_by_name(). Does this make any sense? Does this help, or am I missing the point of your question?
Ryan Wednesday, 12 Aug, 2009 Posted at 09:21AM
Gabe – thanks a bunch for the response. Moving from fixtures to factories might be toying with my expected behavior a bit, as with fixtures,
users(:ryan)would return the “ryan” object from the DB. I’m not saying that’s what I want, but more-or-less just saying.And yes, I could very well use
User.find_by_name()or the like, but that would require knowledge of the actual data in the factory (i.e. the “name”), which I’d like to avoid. I’ve learned early on that it’s best to try and keep tests somewhat dumb when it comes to specific data.Anyway, whether or not I’m doing the right thing, I’m using sequences to get around my validation issues.
That could very well be what you’re supposed to do to avoid unique validation errors, but it just didn’t feel right. But as long as that’s okay, I think I’m good to go :-)
Thanks again for the help.
Ryan Wednesday, 12 Aug, 2009 Posted at 09:24AM
Oh, also, I prefer vanilla test/unit as well (and shoulda is okay, too), but the current project I’m referring to has about 350 or so specs, and the cost/benefit ratio to convert them never really appealed to me.
Gabe Hollombe Thursday, 13 Aug, 2009 Posted at 06:08AM
Ryan, I’m glad that maybe my ramblings were semi-helpful. =-)
Using sequences to get around unique validation requirements is totally the right thing to do (as I understand things).
Also, instead of find_by_name, you can always go with something like:
Peter nixey Monday, 16 Aug, 2010 Posted at 11:17PM
Hi Ryan,
I hit exactly the same problem as this and it took me the whole of today to discover what was a frustratingly simple solution. I hope this answers your problem too.
The transaction in the test is only defined by the “it should do… end” block. If you don’t wrap your factory creation in that block then it’s not rolled back and remains in the database.
I posted the original question and the subsequent answer on stackoverflow: http://stackoverflow.com/questions/3497135/why-isnt-factory-girl-operating-transactionally-for-me-rows-remain-in-databas/3498959#3498959