FactoryGirl, 2 times creates user? - ruby-on-rails-3

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

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

Rails FactoryGirl Duplicated Factory

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

Factory girl, dependent factories

UPDATE
I went back to using Fixtures. IMOP, fixtures are FAR better than factories; easier to use, easier to write, easier to understand (no magic). My suggestion: limit your testing library to the very basics (listen to DHH)...use minitest with fixtures.
original post
In my app a district has many schools, a school has many uses, a user has many accounts, an account has one role. In order to create complete factories for testing I need to create a user and school that persists across factories. Im getting a "stack level too deep" error in my recent attempts.
My user_test.rb
FactoryGirl.define do
factory :district do
name "Seattle"
end
factory :school do
association :primarycontact, factory: :user # expecting this to attach the user_id from factory :user as :primary contact_id in the school model
association :district, factory: :district # expecting this to attach the :district_id from the :district factory as :district_id in the school model
name "Test School"
end
factory :user do, aliases: [:primarycontact]
email "adam#example.com"
name "Who What"
username "wwhat"
password "123456"
password_confirmation { |u| u.password }
association :school, factory: :school # expecting this to create :school_id in the users model, using the :school factory
end
factory :role do
name "student"
end
factory :account do
association :user, factory: :user
association :role, factory: :role
end
end
So, I am attempting to do FactoryGirl.create(:account)... which I am expecting to create an account, with the user and role from the factories above, with the user associated with the school that is associated with the district. This is not working for me. Among failing tests I get a "stack level too deep" error. And, I believe my before each DatabaseCleaner.clean is clearing the test db before each new factory.
The test that calls these factories is:
describe "User integration" do
def log_em_in
visit login_path
fill_in('Username', :with => "wwhat")
fill_in('Password', :with => "123456")
click_button('Log In')
end
it "tests log in" do
user = FactoryGirl.create(:account)
log_em_in
current_path.should == new_user_path
end
end
.
current_path.should == new_user_path returns unknown method error 'should'
How can I improve this code to nest the factories correctly and get a current_user in order to continue testing?
MODELS
school.rb
belongs_to :district
belongs_to :primarycontact, :class_name => "User"
has_many :users, :dependent => :destroy
user.rb
belongs_to :school
has_many :accounts, :dependent => :destroy
district.rb
has_many :schools
account.rb
belongs_to :role
belongs_to :user
role.rb
has_many :accounts
has_many :users, :through => :accounts
Your basic problem is that you have a circular dependency between your user factory and your school factory, caused by the fact that you create a primarycontact (a user) when you create a school, then that user creates a school, and so on.
You can get around this by changing how you define your school association inside the user factory. Before doing that though, I'd suggest as a general rule using the shorthand notation for associations. So replace this:
factory :account do
association :user, factory: :user
association :role, factory: :role
end
with this:
factory :account do
user
role
end
Using this simplification, the following factories will do what you want without generating any circular dependency:
FactoryGirl.define do
factory :district do
name "Seattle"
end
factory :school do |school|
district
primarycontact
name "Test School"
after_build do |s|
s.primarycontact.school = s
end
end
factory :user do
email "adam#example.com"
name "Who What"
username "wwhat"
password "123456"
password_confirmation { |u| u.password }
school
end
factory :primarycontact, class: "User" do
# add any attributes you want the primarycontact user to have here
end
factory :role do
name "student"
end
factory :account do
user
role
end
end
Notice that what I have done is to create a factory for primarycontact with the class: "User" option. Unlike the user factory, this factory does not create the school by default, avoiding the circular dependency.
Then in the school factory, I use an after_build callback to assign the school itself to the school association on primarycontact, rather than creating a new school (which was causing the problem in your factories).
Hope that makes sense. Note that the callback syntax has changed in the more recent version of factory_girl, see the documentation for details.

What things do I need to know when creating model from another controller and mass-assignment

I have a model called DefaultCompany that has no controller, instead I create it through the companies_controller which calls the user.set_default_company (defined below) if they check the "default company" checkbox on the form.
Default company is a joining table of user_id and company_id.
class DefaultCompany < ActiveRecord::Base
attr_accessible :company_id, :user_id
belongs_to :company
belongs_to :user
end
I keep getting the following error:
Can't mass-assign protected attributes: company, user
app/models/user.rb:22:in `set_default_company'
app/controllers/companies_controller.rb:23:in `create'
I've set my user model to be able to accept nested attributes for DefaultCompany, like this
class User < ActiveRecord::Base
has_one :default_company
accepts_nested_attributes_for :default_company
attr_accessible :default_company_attributes
def set_default_company(company)
exists = DefaultCompany.find(self.id)
if exists
exists.update_attributes(company: company)
else
DefaultCompany.create(company: company, user: self)
end
end
end
And here is the create action for the companies_controller.rb
def create
#company = Company.new(params[:company])
if #company.save
if params[:default_company]
current_user.set_default_company #company.id
end
flash[:notice] = "Company was successfully created."
Role.assign_creator(#company.id, current_user.id)
redirect_to #company
else
redirect_to new_company_path
end
end
So I'm not sure what I need to add so that mass-assignment will pass, can anyone help me figure out / explain this?
I believe rails is strict about the naming in mass-assignment, so although you've whitelisted company_id and user_id, you have not whitelisted company and user.
Try changing the assignment in set_default_company to:
if exists
exists.update_attributes(company_id: company.id)
else
DefaultCompany.create(company_id: company.id, user_id: self.id)
end
You can either change the attr_accessible attributes on Company to :user and :company or set :company_id and :user_id in your set_default_company method call.
Edit:
exists = DefaultCompany.find(self.id)
This seems to be wrong according to your logic.

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/