工厂女孩 - 操纵瞬态属性

时间:2016-04-06 22:30:47

标签: ruby-on-rails factory-bot

我有一个用例,我想根据所包含的特征操作工厂中的瞬态属性。有没有办法做到这一点,还是有更好的方法来做我想要完成的事情?

让我们说我正在构建一个House对象。 house可以包含多个windows。我想创建一些自动创建一些窗口的子工厂,但我希望能够为特定类型的窗口添加特征window_types transient属性实际上是window工厂的特征列表。

factory :house do
  floors 3
  exterior 'Brick'

  transient do
    window_types { [:bay, :double_hung] }
  end

  trait :with_picture_window do
    window_types.push(:picture)
  end

  factory :ranch_house do
    floors 1

    after(:create) do |house, evaluator|
      evaluator.window_types.each do |window_type|
        FactoryGirl.create :window, window_type
      end
    end
  end

  factory :mountain_house do
    floors 2
    exterior 'Log'

    after(:create) do |house, evaluator|
      evaluator.window_types.each do |window_type|
        FactoryGirl.create :window, window_type
      end
    end
  end
end

factory :window do
  material 'Glass'

  trait :bay do
    # bay window attributes
  end

  trait :double_hung do
    # double hung window attributes
  end

  trait :picture do
    # picture window attributes
  end
end

这会在NoMethodError: undefined method 'push' for #<FactoryGirl::Declaration::Implicit>特征中引发:with_picture_window错误。

为了论证,假设我需要这些房屋的不同工厂。有没有办法可以在特征中修改 window_types 瞬态属性,然后将这个瞬态属性反映在我的子工厂中?

我希望能够做到:

FactoryGirl.create :ranch_house
# creates a ranch house with only bay and double_hung windows

FactoryGirl.create :ranch_house, :with_picture_window
# creates a ranch house with bay, double_hung, AND picture windows

如果没有,是否有更好的方法可以实现这一目标?

非常感谢提前。

2 个答案:

答案 0 :(得分:2)

我能够根据Ben提出的想法找到合适的解决方案。我使用本地数组变量来存储需要创建的窗口类型。每个窗口的特征为after(:build)块中的数组添加一种窗口。然后,在after(:create)块中,我实际创建了窗口记录。

第一个after(:build)块(不在trait中的块)很重要,因为它会在对象创建之间重置window_types数组。

FactoryGirl.define do
  window_types = []

  factory :house do
    floors 3
    exterior 'Brick'

    after(:build) do
      window_types = []
    end

    after(:create) do |house|
      window_types.each do |window_type|
        FactoryGirl.create(:window, window_type, house: house)
      end
    end

    trait :with_picture_window do
      after(:build) do
        window_types << :picture
      end
    end

    trait :with_double_hung_window do
      after(:build) do
        window_types << :double_hung
      end
    end

    trait :with_bay_window do
      after(:build) do
        window_types << :bay
      end
    end

    factory :house_with_bay_and_picture_window, traits: [:bay, :picture]
  end
end

我很好奇听到有关此方法的任何想法。目前,这符合我的需求并且非常灵活。

答案 1 :(得分:1)

我不确定这会回答您的问题,但评论对格式限制无效。也许跟随模式可能有助于解决这个问题?

trait :with_window do
  after(:build) do |house|
    # Create a house window
    house.windows << FactoryGirl.build(:window) #Presume you could also pass a window trait here.
  end
  after(:create) do |room|
    # Clear the windows attached in after(:build) and create one without saving the house again
    house.windows.each do |window|
      window.house_id = house.id
      window.save
    end
    house.reload
  end
end

希望这能以某种方式提供帮助!