Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to user factory girl to create associated lists with a has_many with a validation that requires it on create

In a Rails application, given three models User, Article and Reviewer with the following relationships and validations:

class User < ActiveRecord::Base
  has_many :articles
  has_many :reviewers
end

class Reviewer < ActiveRecord::Base
  belongs_to :user
  belongs_to :article
end

class Article < ActiveRecord::Base
  belongs_to :user
  has_many :reviewers

  validate :has_reviewers?

  def has_reviewers?
    errors.add(:base, "article must have at least one reviewer.") if self.reviewers.blank?
  end
end

And the following factories using the newer DSL:

FactoryGirl.define do

  factory :user do
    name { (8...20).map{ ('a'..'z').to_a[rand(26)] }.join }
    age  { Kernel.rand(100) }
  end

  factory :article do
    body "This is the article content"
    title "This is the title"
    user
    after_create do |article|
      article.reviewers = create_list(:user, 2)
    end
  end

  factory :reviewer do
    user
    article
    state { ["published","draft","rejected","archived"][Kernel.rand(4)] }
  end

end

The factory to create the article doesn't work because the validation fails before the reviewers are created:

> FactoryGirl.create(:article)
ActiveRecord::RecordInvalid: Validation failed: article must have at least one reviewer.

I have made more attempts than I would like to admit trying to overcome this hurdle, but I am stuck! One idea I had was to create the reviewers like this:

  factory :article do
    body "This is the article content"
    title "This is the title"
    user
    reviewers {|a| [FactoryGirl.create(:reviewer, article: a)] }
  end

but in this context, the "a" is not the instance. So that doesn't work either, like it used to.

like image 415
Blizzo Avatar asked May 02 '12 20:05

Blizzo


3 Answers

I reposted this over on the Factory Girl github page as an issue and worked my way around to the answer:

before_create do |article|
  article.reviewers << FactoryGirl.build(:reviewer, article: article)
end

The key was doing it in a before_create, so the validations haven't fired yet, and making sure to push the newly created reviewer into the list of reviews on the instance being created. Thanks to Unixmonkey for responding and keeping me trying new things :)

https://github.com/thoughtbot/factory_girl/issues/369#issuecomment-5490908

like image 193
Blizzo Avatar answered Oct 19 '22 20:10

Blizzo


factory :article do
  reviewers {|a| [a.association(:reviewer)] }
end

or

factory :article do
  before_create do |a|
    FactoryGirl.create(:reviewer, article: a)
  end
end
like image 37
Unixmonkey Avatar answered Oct 19 '22 18:10

Unixmonkey


The new syntax is:

before(:create) do |article|
  article.reviewers << FactoryGirl.build(:reviewer, article: article)
end
like image 1
Michaël Witrant Avatar answered Oct 19 '22 18:10

Michaël Witrant