工厂女孩 - 操纵瞬态属性

Factory Girl - Manipulate Transient Attribute

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

假设我正在构建一个 House 对象。 house可以有多个windows。我想创建一些会自动创建一些 windows 的子工厂,但我希望能够 添加特定类型 window 的特征。 window_types 瞬态属性实际上是 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

这从 :with_picture_window 特性中抛出一个 NoMethodError: undefined method 'push' for #<FactoryGirl::Declaration::Implicit> 错误。

为了论证,假设我确实需要不同的工厂来建造这些房子。有没有一种方法可以修改特征中的 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

如果没有,有没有更好的方法可以完成此操作?

非常感谢。

我不确定这是否会回答您的问题,但由于格式限制,评论将毫无用处。也许下面的模式可以解决这个问题?

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

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

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

第一个 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

我很想知道关于这种方法的任何想法。现在,这很适合我的需要,而且非常灵活。