使用 FactoryBot 创建具有 has_one 关联的用户

3

背景:

我正在尝试创建一个与has_one/belongs_to相关的FactoryBot对象。

 User has_one Car

 Car has_one Style

 Style has an attribute {style_number:"1234"}

问题

我的控制器引用了用户,用户拥有一个汽车,汽车又拥有一个样式,我需要在FactoryBot中设置这些值。

我该如何创建一个用户,同时拥有一个具有样式对象的汽车对象?

我已经阅读了文档https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md

然而,我不理解他们建议如何做。我已经弄清楚了需要嵌套三个对象,但是对语法感到困惑。

控制器

before_action :authenticate_user!
before_action :set_steps
before_action :setup_wizard    

include Wicked::Wizard

def show
    @user = current_user
    @form_object = form_object_model_for_step(step).new(@user)
    render_wizard 
end

private

def set_steps
    if style_is_1234
        self.steps = car_steps.insert(1, :style_car)                
    else
        self.steps = car_steps
    end
end

def style_is_1234
    if params.dig(:form_object, :style_number)
        (params.dig(:form_object, :style_number) & ["1234"]).present?
    else
        (current_user.try(:car).try(:style).try(:style_number) & ["1234"]).present?
    end
end

def car_steps
    [:type,:wheel, :brand]
end

Rspec测试

用户工厂

FactoryBot.define do
  factory :user, class: User do
    first_name { "John" }
    last_name  { "Doe" }
    email { Faker::Internet.email }
    password { "somepassword" }
    password_confirmation { "some password"}
  end
end

测试方法前置处理

 before(:each) do
      @request.env["devise.mapping"] = Devise.mappings[:user]
      user = FactoryBot.create(:user)
      sign_in user

测试

context "Requesting with second step CarStyle" do 
            it "should return success"  do
              get :show, params: { :id => 'car_style' }
              expect(response.status).to eq 200
            end
          end

目前该测试失败,因为User.Car.Style.style_number未设置为“1234”。

试验1 (https://github.com/thoughtbot/factory_bot_rails/issues/232)

FactoryBot.define do
      factory :user, class: User do
        first_name { "John" }
        last_name  { "Doe" }
        email { Faker::Internet.email }
        password { "somepassword" }
        password_confirmation { "some password"}
        car
      end
    end

FactoryBot.define do
      factory :car, class: Car do
        make { "Holden" }
        model  { "UTE" }
      end
    end


FactoryBot.define do
      factory :style, class: Style do
        color { "blue" }
        for_car

        trait :for_car do
          association(:styable, factory: :car)
        end
      end
    end

来自Trail 1的错误

SystemStackError: 栈级别太深

Trail 2

我尝试了srng的建议

编辑:对于多态关联,请尝试;

FactoryBot.define do
  factory :car, class: Car do
    make { "Holden" }
    model  { "UTE" }
    association :stylable, factory: :style
  end
end

并且出现了错误:

ActiveRecord::RecordInvalid:验证失败:Stylable必须存在

我认为这是一个Rails 5问题。https://github.com/rails/rails/issues/24518

但是,我想保留我的代码,并添加optional:true。有什么方法可以做到这一点吗?

Trail 3

FactoryBot.define do
   factory :car, class: Car do
     make { "Holden" }
     model  { "UTE" }
     after(:create) do |car|
        create(:style, stylable: car)
     end
   end
 end

我尝试了Srng的第二个建议,虽然它对他有效,但我得到了一个略微不同的错误:

ActiveRecord :: RecordInvalid: 验证失败:用户必须存在

2个回答

1
为了创建依赖工厂,您需要为每个模型创建一个工厂,然后只需将依赖的Model名称添加到您的工厂中,例如。
    FactoryBot.define do
      factory :user, class: User do
        first_name { "John" }
        last_name  { "Doe" }
        email { Faker::Internet.email }
        password { "somepassword" }
        password_confirmation { "some password"}
        car
      end
    end

FactoryBot.define do
      factory :car, class: Car do
        make { "Holden" }
        model  { "UTE" }
        style
      end
    end


FactoryBot.define do
      factory :style, class: Style do
        color { "blue" }
      end
    end

编辑:

相关代码;

# Factories
FactoryBot.define do
      factory :user, class: User do
        first_name { "John" }
        last_name  { "Doe" }
        email { Faker::Internet.email }
        password { "somepassword" }
        password_confirmation { "some password"}
        after(:create) do |user|
          user.car ||= create(:car, :user => user)
        end
      end
    end

  factory :style, class: Style do
    style_number { "Blue" }
  end

  factory :car, class: Car do
    name { "Holden" }
    trait :style do
      association :stylable, factory: :style
    end
end

#models
class Car < ApplicationRecord
  has_one :style, as: :styleable
end

class Style < ApplicationRecord
  belongs_to :styleable, polymorphic: true
  belongs_to :car
end

# Migrations - The belongs_to is the only important one
class CreateStyles < ActiveRecord::Migration[5.2]
  def change
    create_table :styles do |t|
      t.string :style_number
      t.belongs_to :stylable, polymorphic: true
      t.timestamps
    end
  end
end

class CreateCars < ActiveRecord::Migration[5.2]
  def change
    create_table :cars do |t|
      t.string :name
      t.timestamps
    end
  end
end

尝试了你的示例,但是出现了相同的错误: ActiveRecord::RecordInvalid: 验证失败:必须存在Stylable。 - user2012677
看到修改后的答案,我已经在本地测试过了,没有出现任何错误,因此正在创建。不过,我对你拥有的大量私有方法并不确定。 - ShatteredDev
是的,你的模型长什么样子?你在模型中使用了新的可选参数吗? - user2012677
我意识到我得到了一个稍微不同的错误,现在需要存在用户。 - user2012677
1
嗯,我删除了所有我编写的示例,当我重新编写它们时,它突然就出问题了...现在我已经找回来了,但是使用了不同的解决方案。我将发布所有相关代码。我没有包括用户,因为多态样式->汽车关系是引起麻烦的原因。 - ShatteredDev
显示剩余4条评论

0

可以使用工厂中的瞬态块来实现另一种方式。希望下面的代码片段能够帮助您以新的方式进行探索。

注意:这未经过测试。

## To Create a user in test case
# create(:user) # defaults to 1234 style number
# create(:user, car_style_number: 5678)

DEFAULT_STYLE_NUMBER = 1234

FactoryBot.define do
  factory :user do
    transient do
      car_style_number { DEFAULT_STYLE_NUMBER }
    end

    first_name { "John" }
    last_name  { "Doe" }
    email { Faker::Internet.email }

    after(:create) do |user, evaluator|
      user.car = create(:car, car_style_number: evaluator.car_style_number, user: user)
    end
  end
end

FactoryBot.define do
  factory :car do
    transient do
      car_style_number { DEFAULT_STYLE_NUMBER }
    end

    make { "Holden" }
    model  { "UTE" }
    after(:create) do |car, evaluator|
      car.style = create(:style, style_number: evaluator.car_style_number, car: car)
    end
  end
end

FactoryBot.define do
  factory :style do
    style_number { DEFAULT_STYLE_NUMBER }
  end
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接