Life without fixtures
All the cool kids tell me that fixtures just aren’t the “in” thing anymore. Speaking of which, when did fanny-packs stop being cool? I wish someone would have said something…
Anyway, while I still haven’t fallen out of love with (foxy) fixtures, I have taken some of the home-grown alternative methods for a spin, but so far, I’ve been slightly frustrated with my experience.
The predominant replacement seems to be the “factory” pattern, talked about by Dan Manges and then followed up by the FixtureReplacement plugin. The basic idea is that all your tests/specs should setup the data that they require, and there is a factory that makes it easy to create the necessary valid test data. For example, a homegrown solution might look like this:
class Generate
def self.person(options={})
p = Person.create!({
:name => 'Brandon',
:login => 'brandon',
:password => 'testing'
}.merge(options))
p.account ||= Generate.account
end
end
describe Person, '#authenticate' do
it 'should return the person record if successful' do
person = Generate.person
Person.authenticate(person.login, 'testing').should == person
end
end
This method scales pretty well. There are some issues, such as the use of create!. Sometimes you intentionally want an invalid record, or you want a new record with just some valid attributes initialized. It’s also slower than fixtures, but I don’t care too much about that right now.
But there’s a bigger issue. Like a good little BDDer, I have also been stubbing and mocking all my “unit tests” so there is no interaction with the database or code outside of what is being tested. To make the factory method work, I now need to define 3 different methods depending on what I’m testing: one using #create!, one using #new, and another one using stubbing.
What do we do about it?
I think the FixtureReplacement plugin is on the right track. It handles the new vs. create problem very nicely.
module FixtureReplacement
attributes_for :person do |u|
u.name = String.random
u.login = String.random
u.password = String.random
u.account = default_account
end
end
@person = new_person(:login => 'brandon')
@person = create_person
Before I heard about the FixtureReplacement plugin, I actually concocted my own little solution that handles all three scenarios. I’m not crazy about the syntax, but it works for my needs.
Generate.add Person, :name => 'Brandon', :login => 'brandon' do |generator, u|
u.account ||= generator.account
end
@person = Generate.person(:login => 'brandon')
@person = Generate.new_person
@person = Generate.stub_person
Anywho, for the fixture-less approach to work, I think stubs need to be supported.
Is all this really worth it?
That’s the question I find myself asking. What’s wrong with fixtures anyway?
One thing I really like about fixtures, when done right, is that they help tell the story of your application. You get to know the fixture data as you work on the app.
The factory method also seems to go against convention-over-configuration. Instead of having a default “configuration” when your tests run, you have to configure each test. Call me lazy, but that just seems like too much work.
I find that testing takes a lot more effort now than it did with fixtures. And as a result, I’m more hesitant to test everything. So while the factory method and stubbing is theoretically supposed to help you test better, I feel like they’ve had the opposite effect.
What do you think? Have you had success using the factory pattern for tests?
9 comments
This is probably going to be ginormously long. I apologize in advance. =)
It’s been so long since I’ve used fixtures! I can’t say that I’ve missed them or any of their features. I’m sure that when they are used properly along side stubs and mocks they are much less of a pain, but I apparently wasn’t smart enough (perhaps now I am) to avoid fixture nightmares.
For the past several months I’ve been using the factory pattern with success. There have been some things I’ve found along the way similar to what you’ve mentioned: needing to have methods which build “new” objects and having methods which create “valid” objects. Recently I’ve even split out the valid attributes into their own module, ValidAttributes. My factory uses them to construct objects. ie:
module ValidAttributes def valid_day_attributes { :date => Date.today } end end module Generate def self.new_day(attrs={}) Day.new valid_day_attributes.merge(attrs) end def self.day(attrs={}) day = new_day(attrs) day.save! day end endThe thing I like most about using a factory is that it allows the examples to be very explicit what is needed (or not needed) for a particular behavior. I don’t like having to scan through multiple files to understand an example. I just want to read the example to see all the pertinent information.
Regardless, I need to know what I want to get out of my factory or fixtures or anything else that tries to fill the gap. I need to be able to build/create valid objects, build/create invalid objects, construct intertwined objects (relationships) and also create objects which are a fair real-world representation of a sample set of data. I also want the important pieces to be highly visible in examples, while the non-important pieces are non-visible (or at least not competing with the important stuff for visibility).
With everything though I realize mileage can vary. My experience with factories have been good so far. My experience with fixtures were not so good.
You probably have heard of Shoulda and factory_girl from ThoughtBot. I would recommend giving them a look, as I have found them easy to work with for the factory pattern.
Factory girl is really really nice. Give it a try.
I’ve been using fixture replacement for a little while now and I think it is great. It keeps the data that is relevent to the test right there with the test, making it’s intentions clear. Factory Girl has been getting a lot of hype, but after looking at it briefly, fixture replacements seems cleaner, clearer to me. Has anyone tried both fixture replacements and factory girl and found factory girl to be better?
I’ve been using object_daddy with some success. It adds generate and generate! methods to your model classes so you can do this:
It gives you a centralized location to define valid attributes, which can, of course, be overridden by passing a Hash to the generate! method – which you can use to keep relevant data in sight in your code examples:
“The factory method also seems to go against convention-over-configuration. Instead of having a default “configuration” when your tests run, you have to configure each test. Call me lazy, but that just seems like too much work.”
Actually, one of the reasons I included String.random in FixtureReplacement was for “convention over configuration.” Each test case should express a unique situation – everything else which is irrelevant to your test case should go into before(:each) or elsewhere (FR puts all of it in one file, example_data.rb since the data is so generic).
David – I’d be interested if you used all three and if so, why you prefer ObjectDaddy over the rest. Honestly, I find that putting default values right in the model to be really gross. Thoughts?
One more vote for factory_girl. (Though to be honest I haven’t given the others a fair shake). I don’t really see your point of convention over configuration with factory girl… Aren’t fixture files configuration too? I have one factory.rb file which defines all of my model defaults, with some handy things like sequences for unique emails or usernames, etc… I configure it once, then I can use them anywhere in my tests, relationships and all, and overriding the default objects in individual tests as needed. I haven’t tried working stubs into the mix, but I think where I want to stub, I want to do so explicitly… though a helper method to do so would be nice.
@David: I’m not sure I like the object_daddy idea of a generate method mixed into my models. It seems like something that might interfere with a method name…
Object Daddy += 1
Looks simpler, and more elegant. Object.generate!
@Andrew: The test stuff stays on /spec/exemplars/mode_name_exemplar.rb. So it`s not loaded at runtime. And the methods you end up doing are class methods like: next_valid_name , next_something….
I use fixtures for two things: 1) for automated tests of course 2) and to populate my development database
Maybe I’m wrong for 2) but it helps me have an application with correct and human readable values. I can also recreate a state of the apps easily.
Can you do the same without fixtures?
Speak your mind: