FactoryGirl覆盖关联对象的属性

bob*_*eno 25 ruby rspec associations factory-bot

这可能很简单,但我无法在任何地方找到一个例子.

我有两个工厂:

FactoryGirl.define do
  factory :profile do
    user

    title "director"
    bio "I am very good at things"
    linked_in "http://my.linkedin.profile.com"
    website "www.mysite.com"
    city "London"
  end
end

FactoryGirl.define do 
  factory :user do |u|
    u.first_name {Faker::Name.first_name}
    u.last_name {Faker::Name.last_name}

    company 'National Stock Exchange'
    u.email {Faker::Internet.email}
  end
end
Run Code Online (Sandbox Code Playgroud)

我想要做的是在创建配置文件时覆盖一些用户属性:

p = FactoryGirl.create(:profile, user: {email: "test@test.com"})
Run Code Online (Sandbox Code Playgroud)

或类似的东西,但我不能正确的语法.错误:

ActiveRecord::AssociationTypeMismatch: User(#70239688060520) expected, got Hash(#70239631338900)
Run Code Online (Sandbox Code Playgroud)

我知道我可以通过首先创建用户然后将其与配置文件相关联来实现此目的,但我认为必须有更好的方法.

或者这将工作:

p = FactoryGirl.create(:profile, user: FactoryGirl.create(:user, email: "test@test.com"))
Run Code Online (Sandbox Code Playgroud)

但这似乎过于复杂.是否有更简单的方法来覆盖关联的属性?这是什么语法?

BKe*_*ewl 22

根据FactoryGirl的创建者之一,您无法将动态参数传递给关联助手(在FactoryGirl中关联时设置属性中的传递参数).

但是,您应该可以执行以下操作:

FactoryGirl.define do
  factory :profile do
    transient do
      user_args nil
    end
    user { build(:user, user_args) }

    after(:create) do |profile|
      profile.user.save!
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

然后你几乎可以像你想要的那样打电话:

p = FactoryGirl.create(:profile, user_args: {email: "test@test.com"})
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案.你会更新它以符合最新的Rails版本.例如.我得到了一个"弃权警告:'#ignore`已弃用,将在5.0中删除." 在实施这个答案时. (2认同)

Wal*_*man 6

我认为你可以使用回调和瞬态属性来完成这项工作.如果你修改你的个人资料工厂:

FactoryGirl.define do
  factory :profile do
    user

    ignore do
      user_email nil  # by default, we'll use the value from the user factory
    end

    title "director"
    bio "I am very good at things"
    linked_in "http://my.linkedin.profile.com"
    website "www.mysite.com"
    city "London"

    after(:create) do |profile, evaluator|
      # update the user email if we specified a value in the invocation
      profile.user.email = evaluator.user_email unless evaluator.user_email.nil?
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

那么你应该能够像这样调用它并获得所需的结果:

p = FactoryGirl.create(:profile, user_email: "test@test.com")
Run Code Online (Sandbox Code Playgroud)

不过,我还没有测试过.

  • 我认为你的例子有错误.将`after(:create)`更改为`profile.user.email = evaluator.user_email,除非是evaluateator.user_email.nil?` (2认同)