[rspec-users] Rails' fixtures suck! But what about something like this?

court3nay court3nay at gmail.com
Sun Jul 22 12:55:32 EDT 2007


Please see http://dev.rubyonrails.org/tickets/6424 for further code on  
fixtures and foreign keys.

You might also be interested in the ar_fixtures plugin from topfunky  
to dump fixtures from dev to yaml.

In most cases I'm using mocks and stubbing rather than canned data.  
Its much easier IMO to create a bunch of objects as you need them.

That being said, fixtures are far from a "solved problem" and I look  
forward to your plugin.

-------
Courtenay

On Jul 22, 2007, at 5:39 AM, "Chris Hoffman" <bosshoff at gmail.com> wrote:

> Have you tried just using fixture scenarios?  Did it work?
>
> On 5/21/07, Carl Lerche <carl.lerche at gmail.com> wrote:
>> Sorry about the very long email, but this is a hairy topic that's  
>> been
>> annoying me for some time and I decided to try to do something about.
>> Also, if you got this twice, I apologize too, but it didn't seem to
>> have successfully gone out the first time.
>>
>> Background:
>> ----------
>>
>> I've been dealing with Rails for about a year and a half now. I've
>> been using Rails' built in testing framework, and it's fine...  
>> until I
>> really have to start dealing with the fixtures. I am currently  
>> working
>> on an application that really focuses on selecting data using complex
>> logic. The only way to test the application in a meaningful way is by
>> using a large number of fixtures. This has become a real nightmare to
>> manage and I almost am spending more time keeping the fixtures up to
>> date than I am coding or testing. So, I took a moment, stepped back,
>> and took a good look at how I could I could improve fixtures to make
>> my life a lot easier.
>>
>> I have gotten a quick and dirty prototype of my solution running, but
>> before I really roll up my sleeves and start polishing it up, I
>> thought I would try to get some suggestions from the community.
>>
>> Overview:
>> ---------
>>
>> In my experience, the current fixture framework has three main  
>> problems:
>>
>> - There is no way to create different version of fixtures. What I  
>> mean
>> by this is that you only can have one users.yml file even if your
>> tests depend on different initial conditions. What this usually means
>> is that you will just keep tagging on to the end of users.yml even
>> though it is not the most efficient way to do it.
>> - Managing relationships between tables is a really big pain. One  
>> must
>> manually keep track of IDs. This can quickly become a major headache.
>> - There is no clean way to quickly generate data. If  I want 50
>> records that I could easily generate in a loop, I still gotta write
>> them by hand (at least, I wasn't able to figure out how to add loops
>> to my yaml files in a clean way).
>> - There is a lot of repetition. No way to follow DRY.
>>
>> Solution:
>> ---------
>>
>> + Versioned Fixtures
>>
>> First, to address the fixture "versions", I thought the easiest way  
>> to
>> do this would be scenarios (I was inspired from fixture-scenarios).
>> The way fixtures could be laid out is as follows:
>>
>> + specs
>> |- fixtures
>>   |- global
>>   |- only_one_signed_up_user
>>   |- lots_of_posts_in_los_angeles
>>   |- ...
>>
>> All global fixtures (fixtures that should be loaded before every  
>> spec)
>> go into global, then, for each different scenario, there is a
>> directory created with the relevant fixtures in there. Then, in the
>> specs, you could do the following
>>
>> -------------------------------
>> require File.dirname(__FILE__) + '/../spec_helper'
>>
>> describe User, :when => :only_one_signed_up_user do
>>
>>  it "should be the only user" do
>>    User.count.should == 1
>>  end
>> end
>> -------------------------------
>>
>> The key part here would be the :when => :scenario_name option to the
>> describe method.
>>
>> + Writing the fixtures
>>
>> Writing fixtures with YAML is quite ugly. I think a better way to do
>> it would be using a DSL, but coming up with a good syntax is hard.
>> This is where I am the most hesitant. I have two "options" and I was
>> wondering if people could comment on them and give me some feedback
>> and suggestions.
>>
>> - The first option is how I started out: http://pastie.caboo.se/63359
>>
>> In this option, fixtures for multiple tables can be written in a
>> single file. You would start out by using the _create_ method to tell
>> the parser what table the fixtures are for and you could also specify
>> :with, which would set default attributes for fixtures.
>>
>> You could also nest fixtures in order to easily scope them (see the
>> last user fixture how I specified nested post fixtures).
>>
>> - The second option is what I currently prefer: http://pastie.caboo.se/63361
>>
>> In this option, I keep the one table per file concept that is
>> currently used. As such, we don't need to specify what table we want
>> to use before hand. I make available a with_options method which lets
>> you set default attributes for fixtures. It's also easy to create
>> loops to automatically generate fixtures, as I show in the second  
>> half
>> of the file. The f method is the actual method for creating a  
>> fixture.
>> it takes a name as a string and a block in which the attributes of  
>> the
>> fixtures are specified. However, the method_missing method is
>> implemented to handle any missing methods and use that to create
>> fixtures too as seen with fixture_name { ... }
>>
>> + Dealing with relationships
>>
>> I think what I hate the most about fixtures right now is having to
>> deal with table relationships. Having to keep track of IDs myself is
>> horrible. So, I thought that the easiest way to do this is making the
>> table_name(:fixture_name) method available straight inside the
>> fixtures. So, if I have a user model that has a location associated
>> with it (let's say a zip code, longitude, and latitude), instead of
>> having to copy / paste that data across fixtures, I can do the
>> following:
>>
>> -------------------------------
>> local_guy {
>>  zip_id        zips(:portland)
>>  longitude     zips(:portland).longitude
>>  latitude      zips(:portland).latitude
>> }
>> -------------------------------
>>
>> This is much easier to deal with than the other way around. This also
>> introduces dependencies between fixtures, I thought the easiest way  
>> to
>> handle it would be adding a fixture_order configuration:
>>
>> -------------------------------
>> Spec::Runner.configure do |config|
>>  config.fixture_order = :zips, :categories, :users, :posts
>> end
>> -------------------------------
>>
>> + What's not solved with this
>>
>> I haven't figured out a good way to handle counter_cache columns.
>> Right now, this would still require you to manually count how many
>> fixtures you have associated with a parent and add that to the
>> counter_cache column. Any ideas of a better way to do this?
>>
>> Well, that's all I came up with. I'm going to keep hacking away at
>> this and hopefully you all have some feedback to improve this  
>> concept.
>>
>> --
>> EPA Rating: 3000 Lines of Code / Gallon (of coffee)
>> _______________________________________________
>> rspec-users mailing list
>> rspec-users at rubyforge.org
>> http://rubyforge.org/mailman/listinfo/rspec-users
>>
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users


More information about the rspec-users mailing list