Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get two associations within a Factory to share another association

Tags:

I've got these 5 models: Guardian, Student, Relationship, RelationshipType and School. Between them, I've got these associations

class Guardian < ActiveRecord::Base
  belongs_to :school
  has_many :relationships, :dependent => :destroy
  has_many :students, :through => :relationships
end

class Student < ActiveRecord::Base
  belongs_to :school
  has_many :relationships, :dependent => :destroy
  has_many :guardians, :through => :relationships
end

class Relationship < ActiveRecord::Base
  belongs_to :student
  belongs_to :guardian
  belongs_to :relationship_type
end

class School < ActiveRecord::Base
  has_many :guardians, :dependent => :destroy
  has_many :students, :dependent => :destroy
end

class RelationshipType < ActiveRecord::Base
  has_many :relationships
end

I want to write a FactoryGirl which defines a relationship. Every relationship must have a guardian and a student. These two must belong to the same school. The guardian factory has an association with school, and so does the student factory. I've been unable to get them to be built in the same school. I've got the following code:

FactoryGirl.define do

  factory :relationship do
    association :guardian
    association :student, :school => self.guardian.school
    relationship_type RelationshipType.first
  end

end

This results in the following error when I try to build a relationship using this factory:

undefined method `school' for #<FactoryGirl::Declaration::Implicit:0x0000010098af98> (NoMethodError)

Is there any way to do what I want, to make the guardian and the student belong to the same school without having to resort to passing already created guardians and students to the factory (which is not its purpose)?

like image 243
sauronnikko Avatar asked Jan 11 '12 13:01

sauronnikko


3 Answers

This answer is the first result on Google for 'factory girl shared association' and the answer from santuxus really helped me out :)

Here's an update with the syntax from the latest version of Factory Girl in case anyone else stumbles across it:

FactoryGirl.define do
  factory :relationship do
    guardian
    relationship_type RelationshipType.first

    after(:build) do |relationship|
      relationship.student = FactoryGirl.create(:student, school: relationship.guardian.school) unless relationship.student.present?
    end
  end
end

The unless clause prevents student from being replaced if it's been passed into the factory with FactoryGirl.create(:relationship, student: foo).

like image 153
Simon Avatar answered Oct 15 '22 05:10

Simon


I think this should work:

FactoryGirl.define do
  factory :relationship do 
    association :guardian
    relationship_type RelationshipType.first
    after_build do |relationship|
      relationship.student = Factory(:student, :school => relationship.guardian.school)
    end
  end
end
like image 32
santuxus Avatar answered Oct 15 '22 04:10

santuxus


There is cleaner way to write this association. Answer got from this github issue.

FactoryGirl.define do
  factory :relationship do 
    association :guardian
    student { build(:student, school: relationship.guardian.school) }
    relationship_type RelationshipType.first
  end
end
like image 29
nan Avatar answered Oct 15 '22 04:10

nan