I am using rspec and factory girl and am having a weird problem getting a sub class from a factory. I am creating a designer, which is a sub cat of a user, however the test is still receiving a user, not a designer.
FactoryGirl.define do
factory :user do
factory :designer do
role: "designer"
end
end
end
describe StoreRating do
it "should have a rating" do
user = FactoryGirl.create(:designer)
store = FactoryGirl.create(:store)
StoreRating.create(designer: user, store: store, rating: 5)
end
end
Try:
FactoryGirl.define do
factory :user do
factory :designer, class: Designer do
role: "designer"
end
end
end
Related
For example I have two models a user and a post. A post belongs_to a user and a user has many posts
#spec/factories/post.rb
FactoryBot.define do
factory :post do
user
body Faker::Movie.quote
posted_at "2018-04-03 13:33:05"
end
end
#spec/factories/user.rb
FactoryBot.define do
factory :user do
first_name 'Jake'
end
end
Using Rspec in a test I want to do this:
user = create(:user, first_name: 'Barry') #id 1
post = create(:post, user: user)
I would expect that the user_id of post to be 1 however it is creating another user prior and the user_id is 2.
How can you specify the association when you are creating the object with factory_bot / factory_girl?
You should use explicit associations instead of implicit ones:
#spec/factories/post.rb
FactoryBot.define do
factory :post do
association :user # <<<--- here the change
body Faker::Movie.quote
posted_at "2018-04-03 13:33:05"
end
end
#spec/factories/user.rb
FactoryBot.define do
factory :user do
first_name 'Jake'
end
end
https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#associations
Another option is to use #trait method within the parent.
FactoryBot.define do
factory :post do
user nil
body Faker::Movie.quote
posted_at "2018-04-03 13:33:05"
end
end
FactoryBot.define do
factory :user do
first_name 'Jake'
end
trait :with_post do
after(:create) do |user|
create(:post, user_id: user.id)
end
end
end
FactoryBot.create(:user, :with_post)
Here we have another solution in case your association name and factory name is different then you can follow below syntax.
#spec/factories/post.rb
FactoryBot.define do
factory :post do
association :author, factory: :user
body Faker::Movie.quote
posted_at "2018-04-03 13:33:05"
end
end
in case your factory name is author and model name is user then you can use above syntax
I have two ActiveRecord models User and posts. This is my user model.
class User < ActiveRecord::Base
has_many :posts
end
and this is my Post model.
class Post < ActiveRecord::Base
belongs_to :user
after_initialize :set_name
private
def set_name
self.name = "Post #{self.user.posts.count + 1}"
end
end
all this is working fine but when I write my factories
FactoryGirl.define do
factory :post do
content 'blah blah'
user
end
factory :user do
name 'Dummy name'
end
end
and this is my post_spec.rb file
require 'rails_helper'
RSpec.describe Post do
context 'with valid values' do
it 'should be valid' do
expect(build(:post)).to be_valid
end
end
end
and my test case fails saying that
undefined method posts for nil class in set_name
I don't know where I'm going wrong.
patkoperwas is correct, you're attempting to initialize a Post before you have an associated User object, which your after_initialize demands exist first. If you must use after_initialize, you could create a factory that creates a User before creating a Post and build with that instead.
FactoryGirl.define do
factory :user do
name "blah blah"
factory :user_with_posts do
transient do
post_count = 1
end
after(:create) do |user, evaluator|
evaluator.post_count.times do
create :post, user: user
end
end
end
end
end
I don't believe there is before(:build) functionality built into FactoryGirl. So you can't really use a callback to create a User before building your Post object. I would either create a user_with_post or explicitly create a user and pass it in when you create a post object.
RSpec.describe Post do
context 'with valid values' do
it 'should be valid' do
user = FactoryGirl.create :user_with_posts
post = user.posts.first
expect(post).to be_valid
end
end
You need to configure that association https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#associations
FactoryGirl.define do
factory :user do
name 'Dummy name'
end
factory :post do
content 'blah blah'
association :user
end
end
If you create a post using the factory without a user, FactoryGirl will create the user for you, if you create the user before and pass it when creating a post, FactoryGirl will use the user you provided.
You can use with_initialize in your Post factory like this:
factory :post do
content 'blah blah'
user
initialize_with { new(user: user) }
end
The reason for the issue is described in the factory_bot docs: https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#custom-construction
For maximum compatibility with ActiveRecord, the default initializer builds all instances by calling new on your build class without any arguments. It then calls attribute writer methods to assign all the attribute values.
This is why the Post factory doesn't have the associated User within the after_initialize hook.
I've got 2 factories files
spec/factories/cars.rb
spec/factories/users.rb
A user can have many cars and I would like to create a special trait for this case.
Into my car factory :
FactoryGirl.define do
factory :car do
...
trait :is_blue do
color 'blue'
end
end
factory :blue_car, parent: :car do
is_blue
end
end
My user factory
FactoryGirl.define do
factory :user do
...
trait :with_cars do
cars [ FactoryGirl.create(:blue_car) ]
end
end
factory :user_with_cars, parent: :user do
with_cars
end
end
When I want to use the 'user_with_cars' factory into my 'user_spec' file I've got a 'Factory not registered: blue_car' error
Example :
context 'with cars' do
subject { create(:user_with_cars) }
it 'should make some stuff' do
expect(subject.cars).not_to be_empty
...
end
end
I found a solution.
I change the way I create relations.
In the trait wich add relations I use the 'after(:create)' callback.
spec/factories/users.rb
FactoryGirl.define do
factory :user do
...
trait :with_cars do
after(:create) do |user|
user.cars << create(:blue_car)
end
end
factory :user_with_cars, parent: :user do
with_cars
end
end
My problem is that of FactoryGirl has_many association with validation, with the added complexity of the association being a "transient" attribute, and is quite nested
ie. my classes are (I'm using mongoid, assume Mongoid::Document is included in all models)
class User
has_many :company_admin_profiles
end
class CompanyAdminProfile
belongs_to :company
belongs_to :user # Cannot exist standalone
end
class Company
has_many :company_admin_profiles
validate :has_at_least_one_admin
end
So far with FactoryGirl I've written
FactoryGirl.define do
factory :user do
...
trait(:company_admin) do
transient do
company_admins_count 1
company { create(:company, admins_count: 0) }
end
after(:build) do |user, evaluator|
create_list(:company_admin_profile, evaluator.company_admins_count,
company: evaluator.company,
user: user,
first_name: user.first_name,
last_name: user.last_name,
email: user.email)
end
end
factory :company_admin_user, traits: [:company_admin]
end
end
FactoryGirl.define do
factory :company_admin_profile, class: Company::Admin do
company
end
end
FactoryGirl.define do
factory :company do
transient do
admins_count 1 # need one admin to pass validation
end
after(:build) do |company, evaluator|
build_list(:company_admin_user, evaluator.admins_count,
company: company)
end
end
end
I've tried several variations on this, the last error is
* company - Attribute already defined: company (FactoryGirl::AttributeDefinitionError)
* company_admin_profile - Attribute already defined: company (FactoryGirl::AttributeDefinitionError)
* company_admin_user - Attribute already defined: company (FactoryGirl::AttributeDefinitionError)
I just saw an upvote on the question, so maybe someone is actually interested in the solution I have for now.
I don't remember exactly where the problem was, but here's a code that works :
# factories/company.rb
FactoryGirl.define do
factory :company do
...
transient do
admins_count 1 # need one admin to pass validation
end
after(:build) do |comp, evaluator|
if evaluator.admins_count == 1
build(:company_admin_user, company: comp)
end
end
end
end
# factories/user.rb
FactoryGirl.define do
factory :user do
...
after(:build) do |user|
user.skip_confirmation!
end
# Company admin
trait(:company_admin) do
transient do
company_admins_count 1
company { build(:company, admins_count: 0) }
end
after(:build) do |user, evaluator|
create_list(:company_admin_profile, evaluator.company_admins_count,
company: evaluator.company,
user: user)
evaluator.company.save
end
end
factory :company_admin_user, traits: [:company_admin]
end
end
# factories/company_admin_profile.rb
FactoryGirl.define do
factory :company_admin_profile, class: CompanyAdminProfile do
...
company
end
end
I am having an issue with my classes not being set appropriately when dealing with a Single Table Inheritance class and factory girl.
My Factory is:
factory :team do
name "Test name"
store_password 'password'
end
factory :sales_team, class: Team, parent: :team do
type 'SalesTeam'
end
factory :retail_sales_team, class: Team, parent: :team do
type 'RetailSalesTeam'
end
(I have also tried it with the :sales_team and :retail_sales_team nested inside the factory :team do)
And in my spec I do
#team = FactoryGirl.create(:retail_sales_team, name: "Test Team")
If I then call
#team.class.name #=> "Team"
And when I try to pass #team to an object that
belongs_to :team , class_name: 'RetailSalesTeam'
I get the error
Failure/Error: #sale = FactoryGirl.create(:sale, kpi: #kpi, user: #user, team: #team)
ActiveRecord::AssociationTypeMismatch:
RetailSalesTeam(#70151785667180) expected, got Team(#70151730184960)
Let me know if there is any other information I can provide and thanks in advance!