【问题标题】:Factory Bot HABTM nil value in join table连接表中的 Factory Bot HABTM nil 值
【发布时间】:2019-01-14 17:15:26
【问题描述】:

在为我的 Rails 应用程序运行 rspec(使用 Factory Bot)测试时,我遇到了一个(令人沮丧的!)错误。我看到以下错误:

ActiveRecord::NotNullViolation:
        Mysql2::Error: Field 'group_id' doesn't have a default value: INSERT INTO `groups_users` VALUES ()

它是间歇性的 - 不同的测试会在不同的运行中抛出错误 - 这可能表明存在竞争条件?

我的模型配置为:

User habtm Groups
Groups habtm Users

员工必须属于一个组 - 验证:

validates :groups, presence: { message: 'Staff must be in at least one group' }, if: :staff?

用户

factory :a_staff_member, class: User do
    first_name { Faker::Name.first_name }
    last_name { Faker::Name.last_name }
    username { "#{first_name}_#{last_name}".downcase.gsub(/[^a-z]/i, '') }
    email { "#{first_name}.#{last_name}@example.ac.uk".downcase }
    user_type { :staff }
    groups {|s| [s.association(:group)]}
end

群组

FactoryBot.define do
  factory :group do
    sequence(:title) { |n| "Group Number #{n}" }
    sequence(:short_title) { |n| "GRP#{n}" }
    sequence(:url_slug) { |n| "grp#{n}" }
  end
end

所有失败测试的共同因素是用户创建工厂(并且给定用户的组创建必须属于一个组) - 所以这是我一直集中精力的地方。

鉴于错误出现在 groups_users 表上,我猜它试图在组表中存在组之前将条目保存到连接表?

根据其他SO帖子和互联网拖网,我尝试了各种设置组的方法:

1) groups {|s| [s.association(:group)]}

2) after(:build) {|user| user.groups = [create(:group)]}

3) groups {[FactoryBot.create(:group)]}

用户只是通过实例化

let!(:staff_user) { FactoryBot.create(:a_staff_member) }

在个别规格中 - 所以没有什么令人兴奋的。

但是,无论我如何在工厂中创建小组,它都没有任何区别 - 而且我的想法已经用完了 - 任何指示或建议都将不胜感激!

【问题讨论】:

  • 该验证不起作用,因为它是一个 catch-22。您必须先将用户记录插入数据库,然后才能将任何内容插入连接表,因为连接表需要用户 ID(并且记录在插入之前没有 ID)。您需要从一开始就重新考虑这一点。
  • 感谢您的回复 - 我可能应该补充一下 - 这只是在测试中运行时出现的问题 - 代码本身有效(我可以通过 UI 成功创建用户 - 它也在测试中间歇性地工作套房)。没有循环依赖 - 只要组存在,用户就可以添加到其中。据我所知,这是我在工厂中创建组的方式以及持久化组的方式存在问题,因此可以通过 habtm 关系进行链接。

标签: ruby-on-rails factory-bot rspec-rails has-and-belongs-to-many ruby-on-rails-5.2


【解决方案1】:

我认为尝试通过串联来改变相等性可能会奏效。

我会添加after(:create) { |user| user.groups << create(:group) }

移动到after(:create) 是这里的主要内容,因为您面临的问题似乎还在于尝试为尚未创建的记录添加连接表记录。

【讨论】:

  • 您好,感谢您的建议。不幸的是,这不起作用,因为有一个用户必须属于一个组的验证。在创建后调用组的分配会导致此验证失败。注意,错误消息中的 group_id 为 nil - 好像在保存用户时尝试加入该组之前尚未创建该组......我还尝试将分配切换为串联 - 但这没有效果:(感谢您的建议!
【解决方案2】:

设法追踪问题。在工厂中创建组并分配给用户的点周围放置一些全面的调试消息后,很明显与这些之间的连接表中的 nil 值相关的错误消息实际上是一个红鲱鱼和被工厂外造成的。

我认为问题是由于 Capybara 的测试之间未清理用户会话/登录造成的。

添加:

config.after :each do
  Warden.test_reset!
end

/spec/spec_helper.rb 似乎已经解决了这个问题。希望这次调查中拔掉的头发能长回来!

【讨论】:

    【解决方案3】:

    我认为如果你这样改变它可能会起作用。

     from
     after(:build) {|user| user.groups = [create(:group)]}
    
     to
     after(:create) {|user| user.groups = [create(:group)]}
    

    一旦通过运行“create”创建了用户 ID,则“users.groups”将是可接受的。

    【讨论】:

    • 感谢您的建议 - 不幸的是,这不起作用,因为它是在创建用户后触发的 - 这将导致用户必须属于至少一个组的验证失败。我希望通过在构建后调用创建组,它可以确保保存用户时组记录存在,以便在创建用户时加入记录!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-21
    • 2015-04-25
    • 1970-01-01
    相关资源
    最近更新 更多