Bypass Rails validations when creating FactoryGirl objects
Asked Answered
S

4

28

I have two models (ModelA and ModelB), and FactoryGirl factories for each. I want the factory for ModelB to be able to (A) create test data, and to (B) build (without saving to database) sample data for display to customers. I am having trouble getting (A) to work due to Rails validations in my models.

ModelA:

class ModelA < ActiveRecord::Base
  belongs_to :model_b
  validates_presence_of :model_b
end

Factory for ModelA:

FactoryGirl.define do
  factory :model_a do
    some_attr "hello"
    model_b { FactoryGirl.build :model_b }
  end
end

ModelB

class ModelB < ActiveRecord::Base
  has_one :model_a
end

Factory for ModelB

FactoryGirl.define do
  factory :model_b do
    some_attr "goodbye"
  end
end

I can't create objects from these factories without getting validation errors:

 ruby> FactoryGirl.create :model_a
 ActiveRecord::RecordInvalid: Validation failed: ModelB can't be blank

It appears that FactoryGirl is attempting to save the factory object before saving its assocations. I realize that I could have the factory for ModelB create its associated ModelA (rather than build it) - however, then I would lose the flexibility of being able to use the ModelA factory to either build sample data or save test data. Alternately, I could remove the validations; but then I wouldn't have validations.

Any other options?

Silvasilvain answered 4/10, 2011 at 19:19 Comment(0)
S
17

I looked into a couple solutions.

One is to create a custom proxy, illustrated here: http://codetunes.com/2009/11/05/fixtures-without-validation-with-factory-girl

The other is to set a to_create block in the factory:

FactoryGirl.define do
  factory :model_a do
     model_b { FactoryGirl.build :model_b }       

     to_create do |instance|
       instance.model_b.save!
       instance.save!
     end 
  end
end
Silvasilvain answered 5/10, 2011 at 4:39 Comment(2)
Great fix. Helped me a lot.Springclean
The custom proxy no longer works FYI. default_strategy was removed here: github.com/thoughtbot/factory_girl/commit/…Decidua
C
33

How about this?

FactoryGirl.build(:model_a).save(validate: false)

EDIT: As Scott McMillin comments below, if you want the built object as a variable, you can to do this:

model_a = FactoryGirl.build(:model_a)
model_a.save(validate: false)
Crescent answered 22/12, 2013 at 13:57 Comment(2)
Don't know about the OP but this helped me! Thanks!Oneman
Note: This one-liner returns a TrueClass and not a ModelA instance. If you need to use the instance do: model_a = build(:model_a); model_a.save(validate: false)Rotator
S
17

I looked into a couple solutions.

One is to create a custom proxy, illustrated here: http://codetunes.com/2009/11/05/fixtures-without-validation-with-factory-girl

The other is to set a to_create block in the factory:

FactoryGirl.define do
  factory :model_a do
     model_b { FactoryGirl.build :model_b }       

     to_create do |instance|
       instance.model_b.save!
       instance.save!
     end 
  end
end
Silvasilvain answered 5/10, 2011 at 4:39 Comment(2)
Great fix. Helped me a lot.Springclean
The custom proxy no longer works FYI. default_strategy was removed here: github.com/thoughtbot/factory_girl/commit/…Decidua
L
2

This circular validation is not a good idea. How are you intending to build real objects in the database? Imho that is simply impossible.

You need to save objects to the database first, the ID is assigned by the database and only after building/saving the object can you refer to the object using the ID in a foreign-key. So in order to be able to refer to an record in the database, it has to be saved first. But if the validations require the ID's to be known and present before saving ...

In a belongs_to relation, the foreign key is in the table, and then you can express the need for the key to be present. With the has_one relationship, the foreign-key is in the other table, and you can't express the validation that the element should be present. So you should remove the validation from ModelB.

Hope this helps.

[UPDATE]

Did you try writing:

FactoryGirl.define do
  factory :model_a do
    some_attr "hello"
    model_b
  end
end
Loki answered 4/10, 2011 at 22:20 Comment(2)
Hi - thanks for your response. You are right, I shouldn't have had the circular validations. I've updated my original post. With the corrections, my original problem still remains: I cannot create factory instances of ModelA using FactoryGirl.create :model_a.Silvasilvain
I followed your latest advice and replaced my lazy attribute with association :model_b, :method => :build - this is just what I needed! Thank you.Silvasilvain
C
1

I encountered the same issue as well, that the model fails to save when it's associations failed to save.

Perhaps try this: https://github.com/thoughtbot/factory_girl/issues/369?

Croce answered 16/5, 2016 at 3:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.