Rails FactoryGirl Duplicated Factory - ruby-on-rails-3

I have the following factories defined in my factories.rb file:
require 'factory_girl'
FactoryGirl.define do
sequence(:email) {|n| "person-#{n}#example.com" }
factory :country do
...
end
factory :state do
country
...
end
factory :school do
name "Test School"
country
state
end
factory :user do
school
email
...
end
end
When testing in rspec calling FactoryGirl.create(:school) in one of my descriptors causes two schools with the name "Test School" to be created.
I thought the factories defined in factories.rb were just a bunch of unsaved instance objects, can somebody clarify as to why I'm having this issue?
Here's the exact rspec:
require 'spec_helper'
describe "school login" do
it "displays a success message upon successful login to school",do
school = FactoryGirl.create(:school)
user = FactoryGirl.create(:user, :username => "jdoe")
School.all.each do |school|
puts school.name #2x => "Test School"
end
visit school_path(user.school)
click_link('login')
fill_in "username", :with => "jdoe"
fill_in "password", :with => "secret"
click_button "Sign in"
expect(page).to have_selector(".alert-success")
end
end

This line creates the first school
school = FactoryGirl.create(:school)
and this one the second:
user = FactoryGirl.create(:user, :username => "jdoe")
This happens because in your user factory you defined that every user should have a school, so FactoryGirl is creating it for you. If you want your user associated with the first school, you can do something like this:
user = FactoryGirl.create(:user, :username => "jdoe", :school => school)

what's the context code? and how did you find there are 2 schools created?
the code written in ruby files ( factories ) is neither saved to database nor created as object until you declare create(:object) or build(:object).
# Returns a User instance that's not saved
user = FactoryGirl.build(:user)
# Returns a saved User instance
user = FactoryGirl.create(:user)
for more details, refer to : https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#using-factories

Related

FactoryGirl belongs_to with Seeded association

I have a UserType object that ideally is seeded in the DB and remains static:
{id: 1, name: 'Individual'}, {id: 2, name: 'Group'}, {id: 3, name: 'Admin'}
class UserType < ActiveRecord::Base
attr_accessible :name
has_many :users
end
class User < ActiveRecord::Base
attr_accessible :email, :first_name
belongs_to :user_type
end
In testing, I simply want to create an admin user that has its user_type_id field set to 3 when created, and for the UserType.all to have those three items. I've tried a number of things, but here's where I'm at:
FactoryGirl.define do
factory :user_type do
id 1
name "Individual"
trait :group do
after(:create) do |user_type|
id 2
name "Group Leader"
end
end
trait :admin do
after(:create) do |user_type|
id 3
name "Administrative"
end
end
end
end
FactoryGirl.define do
factory :user do
first_name 'TestUser'
email { Faker::Internet.email }
user_type
trait :admin do
after(:create) do |user|
admin_user_type = UserType.where(id: 3).first
admin_user_type = create(:user_type, :admin) unless admin_user_type
user_type admin_user_type
end
end
end
And my test in spec/features/sessions/admin_sign_in_spec.rb:
feature "Admin signing in" do
background do
#institution = create(:institution_with_institutiondomains)
#admin = create(:user, :admin, email: "admin##{#institution.subdomain}.com")
end
scenario "with correct credentials", focus: true do
binding.pry
#admin.inspect
page.visit get_host_using_subdomain(#institution.subdomain)
within("#login-box") { fill_in t('email'), with: #admin.email }
click_button t('session.admin.sign_in') #the action in signing in here checks that user.user_type_id == 3
expect(page).to have_content "You're signed in!"
end
end
In many cases, especially in tests where I have multiple users getting created, I'll receive a MySQL duplicate error on the first id: 1 Individual. I appreciate any guidance.
For what it's worth, anyone finding this may not like my answer, but it is the only thing that works for me. UserTypes are static in my test database, so I removed the traits in the :user_type factory. Instead, I simply set the user_type_id directly and call save on it. Without the save, the change does not persist to my #admin variable. The test data is cleaned between tests using DatabaseCleaner, leaving my user_types table alone.
FactoryGirl.define do
factory :user do
first_name 'TestUser'
email { Faker::Internet.email }
user_type
trait :admin do
after(:create) do |user|
# admin_user_type = UserType.where(id: 3).first
# admin_user_type = create(:user_type, :admin) unless admin_user_type
# user_type admin_user_type
user.user_type_id = 3
user.save #without this, the change won't persist
end
end
end
end

FactoryGirl, 2 times creates user?

I really dont understand of making assotiations. In spec_helper I have got
def log_in_user
user = User.find_by_name 'User1'
user = FactoryGirl.create :user1 unless user
sign_in user
end
in rspec
let(:product) { FactoryGirl.build :product_A }
describe "GET confirm purchase" do
it "xxx" do
log_in_user
Product.should_receive(:find_active_by_id).with("1").and_return(product)
...
end
end
factories.rb
FactoryGirl.define do
factory :user do
encrypted_password 'abcdef1'
confirmed_at Time.now
factory :user1 do
email 'user1#test.com'
name 'User1'
year 1984
end
end
factory :product do
factory :product_A do
name "product A"
association :user, factory: :user1
end
end
end
when I run test case an exception occures:
ActiveRecord::RecordInvalid: Validation failed: Email has already been taken
It looks like user1 is creating 2 times, one in log_in_user and the second one in factory: association :user, factory: :user1
I am right? If yes, how can I solve this? I want to create user and have assotiation defined in factory product
best
When you factory :product_A it is automatically calling the factory for :user1.
Then you factory :user1 again in the log_in_user, but the validation on unique emails is preventing the second :user1 from being created.
I would recommend you make email a sequence like so:
FactoryGirl.define do
sequence :email do |n|
"user#{n}#test.com"
end
factory :user do
encrypted_password 'abcdef1'
confirmed_at Time.now
factory :user1 do
email
name 'User1'
year 1984
end
end
factory :product do
factory :product_A do
name "product A"
association :user, factory: :user1
end
end
end
Then, I would alter the sign_in_user to take an (optional) user as an option like this:
def log_in_user(user)
user =|| User.find_by_name 'User1'
user =|| FactoryGirl.create :user1
sign_in user
end
And modify your test case to pass that user object to the login:
let(:product) { FactoryGirl.build :product_A }
describe "GET confirm purchase" do
it "xxx" do
log_in_user(product.user)
Product.should_receive(:find_active_by_id).with("1").and_return(product)
end
end

How to set child parameter in FactoryGirl 3.3 for has_many association?

There are user and user_level in our rails app. User has_many user_levels. We build FactoryGirl data set as following:
factory :user_level do
position "admin"
user
end
factory :user do
login 'testuser'
password "password1"
#user_levels
after(:build) do |user|
user.user_levels << FactoryGirl.build(:user_level, :user => user)
end
end
The question is how to reset the position in user_level, for example, to sales? The following did not work:
ul = FactoryGirl.attributes_for(:user_level, :position => 'admin')
user = FactoryGirl.create(:user, :login => 'test', :password => 'password',
:user_levels => ul)
The error is "undefined method 'each'". Replacing attributes_for with build generates the same error. what's the right way to reset position in user_level for the factory? Thanks.
Your error has nothing to do with FactoryGirl but with pure rails. You're adding a single model instance to a :has_many relation, which should be fed by an array, so try:
ul = FactoryGirl.attributes_for(:user_level, :position => 'admin')
user = FactoryGirl.create(:user, :login => 'test', :password => 'password',
:user_levels => [ul])
There are a couple of ways to reset child parameters in user level.
Added 2nd user level (or more) with reset parameters while keeping the default user_level defined in after(:build) callback.
FactoryGirl as following:
factory :user_level do
position "admin"
user_id 1
end
factory :user do
login 'testuser'
password "password1"
#user_levels
after(:build) do |user|
user.user_levels << FactoryGirl.build(:user_level, :user => user)
end
end
ul = FactoryGirl.build(:user_level, :position => 'sales')
user = FactoryGirl.create(:user, :user_levels => [ul])
The above created user will have two positions, one is 'admin' as default, the 2nd one is position of 'sales' which is the one with reset position value.
user_level needs to be fed into the user creation. All user_level parameters are available for reset.
FactoryGirl as following (without callback in user):
factory :user_level do
position "admin"
user_id 1
end
factory :user do
login 'testuser'
password "password1"
end
ul = FactoryGirl.build(:user_level, :position => 'sales')
user = FactoryGirl.create(:user, :user_levels => [ul])
The above created user will have only one position 'sales'. You are free to add more user levels to the user creation as needed.

Got error : can't be blank, Rails test

I would like to do testing my rails application, especially on controller, here are the code
customer_controller_test.rb
require 'test_helper'
class CustomersControllerTest < ActionController::TestCase
include Devise::TestHelpers
setup do
#user = FactoryGirl.create(:user)
#role = FactoryGirl.create(:role)
#puts #role.name
#permission = FactoryGirl.create(:permission)
#puts #permission.name
#role_permission = FactoryGirl.create(:role_permission)
#puts #role_permission.role_id
#puts #role_permission.permission_id
sign_in #user
#customer = FactoryGirl.create(:customer)
end
test "should get index" do
get :index
assert_response :success
assert_template 'index'
assert_not_nil assigns(:customers)
end
test "should show article" do
get :show, :id => #customer.to_param
assert_response :success
assert_template 'show'
assert_not_nil assigns(:customer)
assert assigns(:customer).valid?
end
test "should get new" do
#login_as(#user)
get :new
assert_response :success
end
end
factories.rb <-- setup tha fixtures with factorygirl
FactoryGirl.define do
sequence :email do |n| "admin#admin.admin#{n}" end
sequence :role_name do |n| n end
sequence :role_id do |n| n end
sequence :permission_id do |n| n end
factory :user do |u|
u.name "Admin"
u.role_id {1}
u.email do FactoryGirl.generate(:email) end
u.password "123456"
u.after(:create) do |user|
user.creator_id {1}
user.save
end
end
factory :customer do
name "Test customer-name"
code "Test customer-code"
address "Test customer-address"
phone "Test customer phone"
end
factory :permission do
name "Customer"
end
factory :role do
name do FactoryGirl.generate(:role_name) end
end
end
And I got error
Any idea? Thx before
Try creating the role in FactoryGirl before assigning it to the user. It looks like you've got a validation error when saving your User record, because it's trying to validate that you've got an actual role assigned. You don't, because you're attempting to create your role after your user.
Just try switching the two lines in your FactoryGirl "setup" like this:
#role = FactoryGirl.create(:role)
#user = FactoryGirl.create(:user)
Or, check out this link for a more thorough examination of how to use FactoryGirl to test associations:
http://blog.joshsoftware.com/2011/05/13/testing-associations-with-factory-girl/

Rails 3 + Rspec 2: Validation failed: Email has already been taken

I have 2 models, User and Bucket. User has_many Buckets and a Bucket belongs_to a User.
In factories.rb, I have:
Factory.define :user do |user|
user.email "teste#test.com"
user.password "foobar"
user.password_confirmation "foobar"
end
Factory.sequence :email do |n|
"person-#{n}#example.com"
end
Factory.define :bucket do |bucket|
bucket.email "user#example.com"
bucket.confirmation false
bucket.association :user
end
and I have a login_user module as follows:
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = Factory.create(:user)
##user.confirm!
sign_in #user
end
end
I am using Spork and Watch and my Buckets_controller_spec.rb is as simple as:
describe "User authenticated: " do
login_user
#bucket = Factory(:bucket)
it "should get index" do
get 'index'
response.should be_success
end
...
end
The error is always the same:
Failures:
1) BucketsController User authenticated: should get index
Failure/Error: Unable to find matching line from backtrace
ActiveRecord::RecordInvalid:
Validation failed: Email has already been taken
# ./lib/controller_macros.rb:12:in `block in login_user'
And it only happens when I have the Factory(:bucket). The login works fine when I don't add the Factory(:bucket).
It's always the same error. I have tried adding :email => Factory.next(:email) to the user, but no success.
Edit:
In rails c test:
ruby-1.9.2-p180 :019 > bucket = Factory(:bucket, :email => "hello#hello.com")
ActiveRecord::RecordInvalid: Validation failed: Email has already been taken
ruby-1.9.2-p180 :018 > Bucket.create(:email => "hello#hello.com")
=> #<Bucket id: 2, email: "hello#hello.com", confirmation: nil, created_at: "2011-04-08 21:59:12", updated_at: "2011-04-08 21:59:12", user_id: nil>
Edit 2:
I found out that the error is in the association, however, I don't know how to fix it.
bucket.association :user
When you define a factory with an association you need to give the factory an object to associate with whenever you use the factory.
This should work:
describe "User authenticated: " do
login_user
#bucket = Factory(:bucket, :user => #user)
it "should get index" do
get 'index'
response.should be_success
end
end
That way factorygirl knows to make a bucket which is associated with #user.
Try this in your user factory:
Factory.define :user do |f|
f.sequence(:email) { |n| "test#{n}#example.com" }
...
end
I think that's probably your problem. When you use f.email = "anyvalue" it's going to use that value every time. I see you were trying to create a sequence in the next block, but I'm not sure that sequence is getting used.
ALSO - be aware that if you get tests interrupted by a crash or something, sometimes bogus test data can get left in your test DB instead of being rolled back.
Very first thing I try if something worked once and then quit working is to reset the test db.
rake db:test:prepare
That will clean everything out.
If this doesn't work let me know and I'll take a second look!
If someone is getting this recently with your views. Try using Database Cleaner.
For more info: RailsTutorial - chapter 8.4.3 - Test database not clearing after adding user in integration test