Here is an example from the FactoryGirl documentation:
FactoryGirl.define do
factory :post do
name "Post name"
user
end
end
In this example, user
is invoking another factory. What I would like to do is effectively call user.id
, but to set it as the definition of an attribute. Here's a stripped-down example:
**models/job.rb**
...
belongs_to :assignee, :class_name => "User"
belongs_to :user
...
attr_accessible :assignee_id, :user_id
...
end
**factories/jobs.rb**
FactoryGirl.define do
factory :job do
assignee_id user.id #what I would like to do, but triggers "undefined method 'id'" error
user_id user.id #user_id is an attribute of the model and is the job assignor
end
I've tried to incorporate the part of the documentation that discusses aliases, but with no luck:
FactoryGirl.define do
factory :user, :aliases => [:assignee] do
....
I feel like (hope?) I'm close here, but any insight is appreciated. Thanks.
EDIT: This code gets my specs running!
**factories/jobs.rb**
FactoryGirl.define do
factory :job do
before(:create) do |job|
user = FactoryGirl.create(:user)
job.assignee = user
job.user = user
end
association :assignee, factory: :user
association :user, factory: :user
sequence(:user_id) { |n| n }
sequence(:assignee_id) { |n| n }
...
end
And it passes my it { should be_valid }
spec, so it seems that the factory is fine, though I think I have some refactoring in the spec itself when I'm calling FactoryGirl.create
.
The code above incorporates the suggestions from mguymon. Thanks!
FINAL UPDATE
After going back and re-reading Hartl's discussion on model associations, I was able to put this matter to rest. What I have above was techincally valid, but didn't actually pass the attributes in properly when i built or created jobs in my spec. Here's what I should have had:
FactoryGirl.define do
factory :job do
association :assignee, factory: :user
user
end
end
My problem also stemmed from how I was creating factories in my spec, so here's how I should have been doing it (but wasn't...sigh):
let(:user) { create(:user) }
before { @job = create(:job, user: @user) }
It seems that I don't explicitly have to have association :user
in my factory, nor do I need the before
block from above.
As an aside, I also learned that I can debug by including puts @job
within an expect
statement, or call @job.assignee_id
to make sure that the attributes are being loaded properly. When that particular spec is run, the puts
statement will output right by the F
or .
from the spec.
If you need to add a set attribute to the factory, you can add it as an alternate state of the factory-using trait. In the example used above, you can add a trait to create an alternate state of the article factory that includes the article publish date. You also need to add a test for the alternate state. You can use it as follows:
This is where the data factory pattern steps into test-driven development. Data Factory (or factory in short) is a blueprint that allows us to create an object, or a collection of objects, with predefined sets of values. You can have multiple sets of predefined values for a single factory.
You can have multiple sets of predefined values for a single factory. In fact, you should create them for each use case of the factory in order to be able to test all use cases of a given method, object or feature.
FactoryBot was built using the Ruby programming language. However, you can use it for multiple frameworks, such as Ruby on Rails, Sinatra, and Padrino. This article covers the implementation of FactoryBot in a Ruby on Rails (i.e. Rails) application.
For the latest version of FactoryGirl, use association
to map to other ActiveRecord Models:
factory :job do
# ...
association :assignee, factory: :user
end
This is straight from the docs.
From the error you posted, it is stating you are trying to get the user.id
but user
is not an ActiveRecord instance but a proxy from FactoryGirl. You will not get this error if you are using the association
method. If you need to directly access a model, you have to manually build it first. You can do this by passing a block in your factory:
factory :job do
assignee_id { FactoryGirl.create(:user).id }
end
It looks like you are trying to associate the same model twice, for this you can use before_create
callback to create a User model and assign to user
and assignee
:
factory :job do
before(:create) do |job|
user = FactoryGirl.create(:user)
job.assignee = user
job.user = user
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With