danielbker danielbker - 3 months ago 13
Ruby Question

factory_girl factory with a transient attribute and callback creates two records

I have the following factories set up

factories/request.rb

FactoryGirl.define do
factory :request do
serves 1
instructions "I want cheap food."
allergens "Nothing special"
active true

transient do
requested_by nil
suburb nil
end

after(:create) do |request, factory|
if factory.requested_by
FactoryGirl.create(:request, customer: factory.requested_by)
end
if factory.suburb
FactoryGirl.create(:request, suburb: factory.suburb)
end
end
end

trait :with_customer do
customer
end

end


factories/customer.rb

FactoryGirl.define do
factory :customer do
address "321 Sample Street"
allergies "Nuts"
dietary_info "Nothing"
account
end

trait :with_request do
transient do
request_count 5
end

after(:create) do |request, evaluator|
create_list(request, evaluator.request_count, customer: customer)
end
end

trait :with_diet do
diet_info factory: :diet_info
end

trait :with_suburb do
suburb
end

end


And I am trying to test that a customer's request is returned with the following Rspec example:

it "returns active request assuming there is one" do
cus = create(:customer)
expect(cus.requests.first).to be_nil

re = create(:request, requested_by: cus)
expect(cus.requests.first).not_to be_nil
# this fails. expected 1, got 2
expect(cus.requests.first.id).to eq 1

expect(re.id).to eq (1)

expect(cus.has_active_request?).to be true
# this fails as well
expect(cus.active_request).to eq(re)

end


Output

expected: #<Request id: 1, customer_id: nil, suburb_id: nil, serves: 1, instructions: "I want cheap food.", all...othing special", active: true, created_at: "2016-08-15 11:33:05", updated_at: "2016-08-15 11:33:05">
got: #<Request id: 2, customer_id: 1, suburb_id: nil, serves: 1, instructions: "I want cheap food.", aller...othing special", active: true, created_at: "2016-08-15 11:33:05", updated_at: "2016-08-15 11:33:05">

(compared using ==)


The last expectation fails as well. It seems like two
Request
objects are created and
cus
and
re
refer to different ones. I don't understand how I managed to create that relationship. This makes testing impossisble because I don't have a reference to the actual request stored in
cus.active_request
(the model method I am trying to test).

Answer

When you call your :request factory with requested_by:, the factory definition creates one Request, your after(:create) creates a second (attached to the Customer you provided), and the factory returns the first. So the Customer you provided is attached to the second Request (ID 2), and the Request that was returned is the first request (ID 1).

To fix it, don't create the request in the second callback, just associate the Customer with it:

after(:create) do |request, factory|
  if factory.requested_by
    request.update_attributes! customer_id: factory.requested_by.id
  end
  if factory.suburb
    request.update_attributes! suburb_id: factory.suburb.id
  end
end

P.S.: I think you want those traits inside the factory do ... end blocks. Doesn't matter for the question you asked, though, since you didn't use the traits anywhere.