通过关联在FactoryBot has_many上出现NoMethodError“每个”

时间:2019-02-07 17:14:58

标签: ruby-on-rails ruby-on-rails-5 factory-bot

我有一家工厂,可以在引擎内生成具有角色的设计用户。用户模型has_many :roles through: :roles_users。我可以使代码与after(:create)子句一起工作,但不能与association:关键字一起工作。

这有效:

app / model / myengine / role.rb

module MyEngine
    class User < ActiveRecord::Base
        has_many :roles_users
        has_many :roles, through: :roles_users
    end
end

spec / factories / roles.rb

factory :role, class: "MyEngine::Role" do
    type: { 'admin' }
end
factory :user, class: "MyEngine::User" do
  sequence(:email) { |n| "tester_#{n}@example.com" }
  password { 'xxx' }
  password_confirmation { 'xxx' }      

  after(:create) do |user|
    user.roles << FactoryBot.create(:role)
  end 
end

但这不是,并且测试在初始化时失败,并显示undefined method 'each' for #<MyEngine::Role:0x0...>

factory :user, class: "MyEngine::User" do
  sequence(:email) { |n| "tester_#{n}@example.com" }
  password { 'xxx'}
  password_confirmation { 'xxx' }      
  association: :roles, factory: :role
end

更新/编辑如下:

FactoryBot文档仅出于可能原因建议了after(:create)钩子。从用户评论来看,上述代码存在两个问题:

  • 不使用集合
  • 关联关联后该对象不存在

使用@Vasfed的建议,可以直接使用集合而不是对象来分配角色关联:

factory :user, class: "MyEngine::User" do
  sequence(:email) { |n| "tester_#{n}@example.com" }
  password { 'xxx'}
  password_confirmation { 'xxx' }      
  roles { [ create(:role) ] }
end

根据@ulferts建议,使用new代替create:

factory :user, class: "MyEngine::User" do
  sequence(:email) { |n| "tester_#{n}@example.com" }
  password { 'xxx'}
  password_confirmation { 'xxx' }      
  roles { [ build(:role) ] }
end

两者都会产生:

ActiveRecord::RecordInvalid: Validation failed: Roles users is invalid

由于模型没有验证,这似乎表明可能由于名称空间解析而导致FK表中缺少记录或找不到FK表的麻烦。

1 个答案:

答案 0 :(得分:1)

错误是因为您要将角色上的单个实例传递给roles而不是集合。 FactoryBot无法知道要为关联创建多少个角色,因此无论如何都需要手动创建它们。

最简单的解决方法是roles { [ create(:role) ] }