Question

My associations aren't so complex but I've hit a wall making them work with FactoryGirl:

Text: blast_id:integer recipient_id:integer

class Text < ActiveRecord::Base
  belongs_to :blast
  belongs_to :recipient, class_name: "User"

  validates :blast_id, presence: true  
  validates :recipient_id, presence: true
end

Blast: content:string author_id:integer

class Blast < ActiveRecord::Base
  belongs_to :author, class_name: "User"
  has_many :texts

  validates :author_id, presence: true
end

User: name:string, etc. etc.

class User < ActiveRecord::Base
  has_many :blasts, foreign_key: "author_id"

  validates :name, presence: true
end

In FactoryGirl I've got:

FactoryGirl.define do 
  factory :user, aliases: [:author, :recipient] do |u|
    sequence(:name)   { Faker::Name.first_name }
  end

  factory :blast do
    author
    content "Lorem ipsum"

    ignore do
      texts_count 1
    end
    after :build do |blast, evaluator|
      blast.texts << FactoryGirl.build_list(:text, evaluator.texts_count, blast: nil, recipient: FactoryGirl.create(:user) ) 
    end
  end

  factory :text do
    blast
    association :recipient, factory: :user
  end
end

Finally, some specs which all fail because Texts is not valid

require 'spec_helper'

describe Text do
  User.destroy_all
  Blast.destroy_all
  Text.destroy_all
  let!(:user) { FactoryGirl.create(:user) }
  let!(:blast) { FactoryGirl.create(:blast, author: user) }
  let(:text) { blast.texts.first }

  subject { text }

  it { should be_valid }

  describe "attributes" do
    it { should respond_to(:blast) }
    it { should respond_to(:recipient) }
    its(:blast) { should == blast }
    its(:recipient) { should == recipient }
  end

  describe "when blast_id is not present" do
    before { text.blast_id = nil }
    it { should_not be_valid }
  end

  describe "when recipient_id is not present" do
    before { text.recipient_id = nil }
    it { should_not be_valid }
  end

end

All the specs fail on FactoryGirl blast creation with:

  1) Text 
     Failure/Error: let!(:blast) { FactoryGirl.create(:blast, author: user) }
     ActiveRecord::RecordInvalid:
       Validation failed: Texts is invalid
     # ./spec/models/text_spec.rb:8:in `block (2 levels) in <top (required)>'

I've tried various iterations of the association code in the FactoryGirl docs and other question answers like this one but my situation is different enough that I can't get it to work.

If you've made it this far, thank you! Super grateful for any leads.

Was it helpful?

Solution

Your factory for "blast" should look like

factory :blast do
  author
  content "Lorem ipsum"

  ignore do
    texts_count 1
  end

  after :build do |blast, evaluator|
    blast.texts << FactoryGirl.build_list(:text, evaluator.texts_count, blast: blast, recipient: FactoryGirl.create(:user) ) 
  end
end

In other words, you immediately create the correct "parent" by connecting the newly created blast to the newly created tekst

To further dry your code, have a look at https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#configure-your-test-suite, describing how to get rid of using "FactoryGirl." over and over again by setting

config.include FactoryGirl::Syntax::Methods 

once in your settings

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top