Pass parameter in setting attribute on association in FactoryGirl
Asked Answered
T

2

33

I want to pass a parameter into a factory that will be used to set the attribute on an associated model. The associated model is created within the factory.

I have a Transaction model with a many-to-many link to itself through a join table TxLink. I want to call link = FactoryGirl.create(:link_red_to_sub, sub: 10, red: 7) which will create two Transaction objects and a TxLink that links the two.

I get an error in the association line below because of the units: sub at the end. The error is "trait not defined". I tried units: { sub } (with brackets) instead but no dice.

factory :tx_link do
    units "9.99"

    factory :link_red_to_sub do
      ignore do
        sub 0
        red 0
      end
      units { red }

      association :giver, factory: :transaction, units: sub
      association :taker, factory: :redemption, units: red
    end
  end
Talmud answered 3/5, 2012 at 15:29 Comment(0)
N
41

The association helper in factory_girl can't take dynamic property arguments, but you can use a dynamic attribute to instantiate the association however you want:

factory :tx_link do
  units "9.99"

  factory :link_red_to_sub do
    transient do
      sub 0
      red 0
    end
    units { red }

    giver { create(:transaction, units: sub) }
    taker { create(:redemption, units: red) }
  end
end

The only downside of this is that the giver and taker associations will use the create strategy regardless of what strategy you're building tx_link with, which essentially means that attributes_for and build_stubbed won't work correctly for the tx_link factory.

The example I provided requires version 4.5.0.

Noaccount answered 4/5, 2012 at 17:34 Comment(1)
ignore is deprecated. Instead, we should use "transient"Algia
C
27

Expanding on Joe's answer to allow differentiation between build and create:

factory :tx_link do
  units "9.99"

  factory :link_red_to_sub do
    ignore do
      sub 0
      red 0
    end
    units { red }

    giver { build(:transaction, units: sub) }
    taker { build(:redemption, units: red) }

    after(:create) do |link_red_to_sub|
      link_red_to_sub.giver.save!
      link_red_to_sub.taker.save!
    end
  end
end
Classicist answered 15/8, 2013 at 21:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.