Jonathon Nordquist
Jonathon Nordquist

Reputation: 2246

FactoryBot #build object with an associated object passed in not properly creating object

I have a Rails app with two models, call them transaction and item. Transactions are created during a checkout flow, and have an associated item object, the transaction holds the details of a sale, and item represents the item being sold.

Models are setup like so:

class Transaction < ApplicationRecord
  belongs_to :item

  validates :item_id, presence: true, uniqueness: true
end

class Item < ApplicationRecord 
  has_one :company_transaction, class_name: 'Transaction'
end

And the factories are setup like so:

FactoryBot.define do
  factory :transaction do
    item { build_stubbed(:item) }
    # Other transaction related attributes
  end
end

FactoryBot.define do
  factory :item do
    # Item related attributes
  end
end

Now, if I create a Transaction object using simple transaction = build(:transaction) I get the transaction, and it works as expected, with an Item object also being created and accessible:

transaction.item_id => #####
transaction.item => #<Item:0x00007fcb08fab628...>
transaction.valid? => true

But, if I create an Item object first, and pass it into the Transaction factory, it exists, sort of, but not as it does with above. If I run something like:

item = build(:item)
transaction = build(:transaction, item: item)

I get

transaction.item_id => nil
transaction.item => #<Item:0x00007fcb08fab628...>
transaction.valid? => false

The Transaction object is no longer valid because the item_id field is nil and this causes test to fail. Does anyone know what I'm doing wrong here? If I use create on the two classes the items are properly built and saved to the database, and the proper fields are updated, but using build this fails. I don't understand why I could get a proper, "full" association using build and allowing the Transaction factory to build it's own Item object, yet if I build my own Item object and pass it in the association is created (as evidenced by transaction.item working) but the associated field is blank, and causing failed validation checks.

Upvotes: 1

Views: 1635

Answers (1)

Jonathon Nordquist
Jonathon Nordquist

Reputation: 2246

After typing all this out, I realized that I could use build_stubbed to create a new instance of the Item class like item = build_stubbed(:item). Passing that into the Transaction factory like transaction = build(:transaction, item: item) is creating the properly working association, and adding a value to transaction.item_id. What I still don't understand is why this is the case, if anyone could enlighten me I'd greatly appreciate it.

Upvotes: 3

Related Questions