Rspec + Factory Girl First Example works, 2nd+ do not - ruby-on-rails

I have an spec like so:
require 'spec_helper'
describe IncomingMailsController do
include Devise::TestHelpers
before(:each) do
#user = Factory.create(:user)
#user1 = Factory.create(:user)
#group = Factory(:group)
#perm1 = Factory.create(:permission, :user => #user)
#perm2 = Factory.create(:permission, :user => #user1)
end
it "xxxxx case 1" do
....
end
it "xxxxx case 2" do
....
end
The first case 1, works fine but then the 2nd fails with:
Failure/Error: #perm1 = Factory.create(:permission, :user => #user)
RuntimeError:
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
# ./spec/factories.rb:23
Does the before each run fresh for each it block?
factories.rb looks like:
require 'factory_girl'
Factory.define :user do |f|
f.sequence(:fname) { |n| "fname#{n}" }
f.sequence(:lname) { |n| "lname#{n}" }
f.sequence(:email) { |n| "email#{n}#google.com" }
f.password "password"
f.password_confirmation { |u| u.password }
f.invitation_code "dmbsrv82811"
end
Factory.define :group do |f|
f.name "myGroup"
f.sequence(:private_email) { |n| "myGroup#{n}" }
end
Factory.define :permission do |f|
f.role_id 1
f.group_id do
(Group.find_by_name('myGroup')).id
end
f.creator_id do
(User.find_by_fname('fname1')).id
end
end
Thanks

On line 23 of factories.rb you have
(User.find_by_fname('fname1')).id
This is hardcoded to find fname1. However, your factory uses a sequence to define fname:
f.sequence(:fname) { |n| "fname#{n}" }
This means that in the second spec, the value for fname will be fname2, not fname1. This is because sequences aren't reset between different specs.
All of this means that when you call User.find_by_fname, it can't find a user with fname1 (only fname2), so it returns nil, which is why you get the called id for nil error.
-- Edit --
Are you doing anything with the user attribute you pass in to the permission factory (for eg with Factory.create(:permission, :user => #user))? If not, I'd change it to something like
Factory.create(:permission, :creator => #user)
Note: if you don't have Permission#creator setup as an association with User you'll need to rather do
Factory.create(:permission, :creator_id => #user.id)
On a similar vein, you might want to define your factory more along the lines of
Factory.define :permission do |f|
f.role_id 1
f.association :group, :factory => :group
f.association :creator, :factory => :user
end
If your associations are setup correctly, there's no need to worry about the ids, the association does the Right Thing for you.

Related

How to call factory method in other file

I am using FactoryGirl in my rails application. I have following code snippet
FactoryGirl.define do
factory :pub_company, :class => Company do |c|
c.name "Dixons Group"
c.address "1500 Martin Ave"
c.city "Santa Clara"
c.state "CA"
end
end
FactoryGirl.buid(:pub_company)
Above code can creates a record fine. I am trying to create more data as similar to above one. so, i am using
require 'factory_girl_rails'
FactoryGirl.define do
factory :new do
FactoryGirl.create(:pub_company)
FactoryGirl.create(:adv_user)
FactoryGirl.create(:adv_setting)
FactoryGirl.create(:adv_ad)
end
end
it fails for me. Is this possible using factorygirl to call a different factory and create a record?
Since, the FactoryGirl is used mostly for testing models, I show the sample models, and factories, spec for the new model.
app/models/new.rb
class New < ActiveRecord::Base
attr_protected :company_id
belongs_to :company
end
app/models/company.rb
class Company < ActiveRecord::Base
attr_accessible :name, :address, :city, :state
has_many :news
end
spec/factories/company_factory.rb
FactoryGirl.define do
factory :company do |c|
c.name "Dixons Group"
c.address "1500 Martin Ave"
c.city "Santa Clara"
c.state "CA"
end
end
spec/factories/new_factory.rb
FactoryGirl.define do
factory :new do
company { FactoryGirl.create :company }
# adv_user { FactoryGirl.create :adv_user }
# adv_setting { FactoryGirl.create :adv_setting }
# adv_ad { FactoryGirl.create :adv_ad }
end
end
NOTE: You shell to have adv_user, adv_setting, adv_ad defined also.
spec/models/new_spec.rb
describe New do
it 'Test new model' do
new_instance = FactoryGirl.create :new
new_instance.pub_company.name.should == "Dixons Group"
...
end
end

Rails 3 - Factory girl and sequence for belongs_to table

I have 2 models - User and Teacher. Teacher belongs_to User, User has Teacher.
So, i use Factory girl gem:
Factory.define :user do |user|
user.user_login "Another User"
user.user_role "admin"
user.password "foobar"
end
Factory.sequence :user_login do |n|
"person-#{n}"
end
Factory.define :teacher do |teacher|
...
teacher.user
end
I met problem and i don't understand how to solve that. When i create user via factory i can easily write:
#user = Factory( :user, :user_login => Factory.next(:user_login) )
And this creates user with inique login.
How can i do same thing for teacher? I tried that:
#teacher = Factory( :teacher, :user_login => Factory.next(:user_login) )
And it doesn't work.
You don't have to specify sequences separately and then pass them to another factory - you can use them inside factories like this:
Factory.define :user do |user|
# ...
user.sequence(:user_login) { |n| "person=#{n}" }
end
or shorter
Factory.define :user do
# ...
sequence(:user_login) { |n| "person=#{n}" }
end
Then, to association a user with teacher:
Factory.define :teacher do
association :user
end
Then you can just call
#teacher = Factory(:teacher)
which will automatically create the associated user with the next user_login in the sequence.
I solved that.
#teacher = Factory( :teacher,
:user => Factory(:user, :user_login => Factory.next(:user_login)) )

Rails 3 Validations - Association Properties, RSpec, and Factory Girl

I'm trying to write a validation class in rails 3 that will check for the presence of at least one associated object that has a particular property value.
In my scenario, an Account can have many Users. The User model has a "Role" string field. An account must have at least one User with a Role that equals "admin".
The following code is breaking during my testing (I'm using rspec & Factory Girl). When the tests are executed, the account_id is returning as null.
Here is the model:
class Account < ActiveRecord::Base
validates :name, :presence => true, :uniqueness => true
validate :has_one_admin
has_many :users
attr_accessible :name
private
def has_one_admin
User.where(:role => "admin", :account_id => self.id).count > 0
end
end
Here is the error being thrown by rspec:
Error message being thrown: Failure/Error: #account = Factory(:account_with_admin)
ActiveRecord::StatementInvalid:
SQLite3::SQLException: no such column: users.account_id: SELECT COUNT(*) FROM "users" WHERE "users"."role" = 'admin' AND "users"."account_id" IS NULL
Here is the RSpec test:
require 'spec_helper'
describe Team do
# - Removed -
# before(:each) do
# #account = Factory(:account)
# #attr = { :name => "Testing Team"}
# end
# Modified after discussion...
before(:each) do
#user = Factory(:user)
#act_attr = { :name => "My Account",
:users => #user }
#account = Account.new(#act_attr)
#attr = { :name => "Testing Team"}
end
# End of modification
it "must have a name" do
no_name_team = Team.new(#attr.merge(:name => ""))
no_name_team.should_not be_valid
end ...
And finally, my factory file:
Factory.define :admin, :class => User do |f|
f.email 'admin#admin.com'
f.password 'password'
f.role 'admin'
end
Factory.define :team, :class => Team do |f|
f.name 'Testing Team'
end
Factory.define :account, :class => Account do |f|
f.name 'Testing Account'
end
Factory.define :account_with_admin, :parent => :account do |f|
f.after_create { |a| Factory(:admin, :account => a) }
end
My assumption is that I'm jumping ahead of the persisting of the object somewhere but I'm just not sure where. Thanks for any help / direction!

Dependent Attributes in Factory Girl

Seems like I should have been able to find an obvious answer to this problem after a few hours of Googling and testing.
I want to be able to set caredate.user_id => provider.user_id within the caredate factory.
Test Error:
ActiveRecord::RecordInvalid:
Validation failed: User must be same
as provider user
I have an ActiveRecord validation which works when tested via the browser:
class Caredate < ActiveRecord::Base //works fine when testing via browser
belongs_to :user
belongs_to :provider
validates_presence_of :user_id
validates_presence_of :provider_id
validate :user_must_be_same_as_provider_user
def user_must_be_same_as_provider_user
errors.add(:user_id, "must be same as provider user") unless self.user_id == self.provider.user_id
end
end
//factories.rb
Factory.define :user do |f|
f.password "test1234"
f.sequence(:email) { |n| "foo#{n}#example.com" }
end
Factory.define :caredate do |f|
f.association :provider
**f.user_id { Provider.find_by_id(provider_id).user_id } //FAILS HERE**
end
Factory.define :provider do |f|
f.association :user
end
My apologies if this has been answered previously; I tried several different options and couldn't get it to work.
Update: This passes validation, so I'm getting closer. I could hack with a random number.
Factory.define :caredate do |f|
f.association :user, :id => 779
f.association :provider, :user_id => 779
end
Factory.define :caredate do |f|
provider = Factory.create(:provider)
f.provider provider
f.user provider.user
end
Try setting the user_id in after_create or after_build:
Factory.define :caredate do |f|
f.after_create { |caredate| caredate.user_id = caredate.provider.user_id }
end

Factory Girl: How to associate a record to another record without creating a new record?

I'm using Factory Girl/Rspec2/Rails 3.
In factories.rb, I have:
Factory.define :user do |user|
user.name 'Some guy'
user.email 'some_guy#somewhere.org'
user.password 'password'
end
Factory.define :org_admin, :parent => :user do |user|
user.email 'org_admin#somehwere.org'
end
Factory.define :user_with_membership_request, :parent => :user do |user|
user.email 'user_with_membership_request#somehwere.org'
end
Factory.define :organization do |org|
org.name 'MEC'
org.description 'Mountain Equipment Co-op'
end
Factory.define :membership do |membership|
membership.user { Factory(:user) }
membership.organization { Factory(:organization) }
end
Factory.define :admin_membership, :parent => :membership do |membership|
membership.user { Factory(:org_admin) }
membership.is_admin true
membership.status 'active'
end
Factory.define :membership_request, :parent => :membership do |membership|
membership.user { Factory(:user_with_membership_request) }
membership.status 'requested'
end
and then in my rspec test I have:
it 'should accept the membership request' do
#org_admin = Factory(:org_admin)
test_sign_in(#org_admin)
#organization = Factory(:organization)
#membership_request = Factory(:membership_request)
put :update, :organization_id => #organization.id, :id => #membership_request.id, :acceptance => 'approve'
...
end
When I run the test, I get:
Failure/Error: #membership_request = Factory(:membership_request)
Validation failed: Name has already been taken
I understand the reason for the failure is because FactoryGirl is creating another organization (with the same name).
But what I'd like to do is create several memberships all associated with the same organization. How do I do that?
Thanks.
Sean
You could check for an existing organization and use it, or create one if none exists:
Factory.define :membership do |membership|
membership.user { Factory(:user) }
membership.organization { Organization.first || Factory(:organization) }
end
FactoryGirl 4+ update:
Factory.define do
factory :membership do
user { create(:user) }
organization { Organization.first || create(:organization) }
end
end
Another approach is to use unique identifiers (e.g.: names) for each factory that you want to reuse, then use initialize_with to generate it:
factory :organization_1 do
ignore { organization_name 'Sample Organization 1' }
name { organization_name }
initialize_with { Organization.find_or_create_by_name(organization_name) }
end
Now any reference to :organization_1 will always retrieve the same Organization. Obviously, you must use distinct names for this to work.
There are two things.
1. You might still want to create unique names for Factory(:organisation) you can achieve that using Factory.sequence which will generate it uniquely for you.
2. You can pass in a Factory(:membership_request, :organization => #organization) to use the existing object instead of creating a new one.
With mongoid you can take combine the use of the #find_or_create_by method with Factory.attributes_for and do something like this
factory :membership do
organization { Organization.find_or_create_by(Factory.attributes_for(:organization))}
end
I'm sure ActiveRecord has something similar.

Resources