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,