简体   繁体   中英

Factory girl after_initialize

I have two ActiveRecord models User and posts. This is my user model.

class User < ActiveRecord::Base
  has_many :posts

end

and this is my Post model.

class Post < ActiveRecord::Base
  belongs_to :user
  after_initialize :set_name


  private
  def set_name
    self.name = "Post #{self.user.posts.count + 1}"
  end

end

all this is working fine but when I write my factories

FactoryGirl.define do
  factory :post do
    content 'blah blah'
    user
  end
  factory :user do
    name 'Dummy name'
  end
end

and this is my post_spec.rb file

require 'rails_helper'

RSpec.describe Post do
  context 'with valid values' do
    it 'should be valid' do
      expect(build(:post)).to be_valid
    end
  end
end

and my test case fails saying that

undefined method posts for nil class in set_name

I don't know where I'm going wrong.

patkoperwas is correct, you're attempting to initialize a Post before you have an associated User object, which your after_initialize demands exist first. If you must use after_initialize, you could create a factory that creates a User before creating a Post and build with that instead.

FactoryGirl.define do
  factory :user do
    name "blah blah"

    factory :user_with_posts do
       transient do
        post_count = 1
       end
       after(:create) do |user, evaluator|
         evaluator.post_count.times do
           create :post, user: user
         end
       end
    end
  end
end

I don't believe there is before(:build) functionality built into FactoryGirl. So you can't really use a callback to create a User before building your Post object. I would either create a user_with_post or explicitly create a user and pass it in when you create a post object.

RSpec.describe Post do
  context 'with valid values' do
    it 'should be valid' do
      user = FactoryGirl.create :user_with_posts
      post = user.posts.first
      expect(post).to be_valid
    end
end

You need to configure that association https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#associations

FactoryGirl.define do
  factory :user do
    name 'Dummy name'
  end

  factory :post do
    content 'blah blah'
    association :user
  end
end

If you create a post using the factory without a user, FactoryGirl will create the user for you, if you create the user before and pass it when creating a post, FactoryGirl will use the user you provided.

You can use with_initialize in your Post factory like this:

factory :post do
  content 'blah blah'
  user

  initialize_with { new(user: user) }
end

The reason for the issue is described in the factory_bot docs: https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#custom-construction

For maximum compatibility with ActiveRecord, the default initializer builds all instances by calling new on your build class without any arguments. It then calls attribute writer methods to assign all the attribute values.

This is why the Post factory doesn't have the associated User within the after_initialize hook.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM