Testing and Fixture Replacements

Hey guys,

I've been practicing TDD on my rails apps for a while now, and I'm
constantly plagued by factory girl and its association support. It
really irks me to see that a test failed because object A calls
a.association ... on object B which calls b.association on ... which
tries to instantiate a duplicate of object C, which already exists,
and so its uniqueness validations fail, causing the whole chain of the
test to explode.

I've been chasing this damn chicken-or-the-egg problem for a while,
but ultimately I'm wondering if it's time to scrap factory girl and
look at something else. Problem is, most of the other alternatives,
Machinist being the primary one I know about, don't appear to have
been updated to work with Rails 3 (and there's no way I'm going back
to 2.x!).

So, that said, can you recommend some relatively recent and up-to-date
fixture replacement frameworks for Rails, or is factory_girl really
about my only option?

Thanks :slight_smile:

I cannot say that I understand the problem you are describing, but
Machinist is working fine for me on Rails 3.

Colin

Phoenix Rising wrote in post #970881:

Hey guys,

I've been practicing TDD on my rails apps for a while now, and I'm
constantly plagued by factory girl and its association support. It
really irks me to see that a test failed because object A calls
a.association ... on object B which calls b.association on ... which
tries to instantiate a duplicate of object C, which already exists,
and so its uniqueness validations fail, causing the whole chain of the
test to explode.

Then your factories are set up incorrectly. This should not be
happening. Let's see some factory code that is giving you trouble.

I've been chasing this damn chicken-or-the-egg problem for a while,
but ultimately I'm wondering if it's time to scrap factory girl and
look at something else.

Doesn't sound like a Factory Girl problem.

Best,

Thank you for the reply, Colin. I should have been more descriptive,
so let me elaborate:

Say I have the following models and factories:

# models
class Game < ActiveRecord::Base
  validates :name, :presence => true, :uniqueness => true
  validates :url, :presence => true, :uniqueness => true
end

class Server < ActiveRecord::Base
  belongs_to :game
  validates :name, :presence => true
  validates :game, :presence => true
end

class Character < ActiveRecord::Base
  belongs_to :server # now we can chain character.server.game
  validates :name, :presence => true
end

# test/factories.rb
Factory.define :game do |g|
  g.name "Foo"
  g.url "www.example.com"
end

Factory.define :server do |s|
  s.name "Bar"
  s.game { |x| x.association(:game) }
end

Factory.define :character do |c|
  c.name "Blah"
  c.server { |x| x.association(:server) }
end

Now, let's say I want to call Factory(:character). It all gets set up
and returned properly as it should. But let's say I have the
following in my factories thereafter:

Factory.define :character2 do |c|
  c.name "Bleh"
  c.server { |x| x.association(:server) }
end

Logically, I'd expect factory_girl to try to create the :server
object, and if it fails, look for it in the database and set the
assignment of that association to what comes out of the DB if
something already exists. But it doesn't do that. Instead, FG will
cause an error in tests (not a failure, an actual error) that will say
that the name and url are already taken. After shaking my fists in
the air and pounding my head on the desk, I reply, "YES, I KNOW
they're taken, that's because I want the association on an EXISTING
OBJECT!"

Am I just plain doing it wrong here? Any input you have would be
appreciated. Thank you!

Yes, I think you are. Have a look at the Faker gem for generating
unique patterns.

Colin

Phoenix Rising wrote in post #970888:

Thank you for the reply, Colin. I should have been more descriptive,
so let me elaborate:

Say I have the following models and factories:

# models
class Game < ActiveRecord::Base
  validates :name, :presence => true, :uniqueness => true
  validates :url, :presence => true, :uniqueness => true
end

class Server < ActiveRecord::Base
  belongs_to :game
  validates :name, :presence => true
  validates :game, :presence => true
end

Server belongs_to Game? Really? Are you sure? If so, why isn't there
a corresponding association on Game? What are you trying to model here?

class Character < ActiveRecord::Base
  belongs_to :server # now we can chain character.server.game
  validates :name, :presence => true
end

# test/factories.rb
Factory.define :game do |g|
  g.name "Foo"
  g.url "www.example.com"
end

Factory.define :server do |s|
  s.name "Bar"
  s.game { |x| x.association(:game) }
end

Factory.define :character do |c|
  c.name "Blah"
  c.server { |x| x.association(:server) }
end

What's with all the constant data? Use Faker and/or sequences to
generate unique values.

Now, let's say I want to call Factory(:character). It all gets set up
and returned properly as it should. But let's say I have the
following in my factories thereafter:

Factory.define :character2 do |c|
  c.name "Bleh"
  c.server { |x| x.association(:server) }
end

Again, I worry about :character2. Looks like you're trying to predefine
every record you need in your factory files -- that is, use them like
fixtures -- which is not how factories are supposed to work. The reason
you can define multiple factories for one class is so you can set up
several default configurations (say, ordinary user and admin user), not
so you can define every record in advance.

Logically, I'd expect factory_girl to try to create the :server
object, and if it fails, look for it in the database and set the
assignment of that association to what comes out of the DB if
something already exists. But it doesn't do that.

No, it doesn't. association *always* creates a new record. Of course,
you can override that by doing Factory :character, :server => @server.
But this is not like fixtures, where it retrieves an existing named
fixture. Nor would you want it to be; it would make factories less
flexible.

You can also use AR model methods to find the existing association
record you want.

Instead, FG will
cause an error in tests (not a failure, an actual error) that will say
that the name and url are already taken. After shaking my fists in
the air and pounding my head on the desk, I reply, "YES, I KNOW
they're taken, that's because I want the association on an EXISTING
OBJECT!"

...which is not what the association method does. Find the object and
use it explicitly.

Am I just plain doing it wrong here? Any input you have would be
appreciated. Thank you!

Yes, you are doing it wrong. You're trying to use factories like
fixtures, and (possibly as a result) you're misunderstanding the
semantics of the association method.

Best,