Factory girl problems accessing certain attributes - ruby-on-rails

I have a user model and an operator model which belongs to user. I created some factories that associate them(in my feature, a user account is created when an operator signs up) so I made this step:
def create_operator_with_user(operator_name)
user = Factory(:user)
puts "MY PASS: #{user.password}"
operator = Factory(:operator, :chief_pilot_or_business_owner => operator_name, :user_id => user.id)
pew = User.find(operator.user_id)
puts "USER: #{operator.user.inspect} PASS: #{operator.user.password} PEWPASS: #{pew.password}"
end
this is my user factory:
Factory.define :admin, :class => User do |f|
f.sequence(:login) { |n| "admin#{n}"}
f.is_admin true
f.password "password"
f.password_confirmation "password"
f.sequence(:email) { |n| "test#{n}#test.com"}
end
Factory.define :user, :parent => :admin do |f|
f.sequence(:login) { |n| "user_#{n}" }
f.sequence(:email) { |n| "the_user_#{n}#asdf.com" }
end
I tried to run my feature and here was the output:
MY PASS: password
USER: #<User id: 181, login: "user_1", email: "the_user_1#asdf.com", crypted_password: "e9f6932a07cbe6e49073a331530f9dc01a3482502d25770be00...", password_salt: "YEjT9Q8EGYdrNh4qGZda", persistence_token: "259a61440f6ecd001e79a4aaf1c5c343e50be04388bbf1718c3...", created_at: "2011-06-02 04:24:34", updated_at: "2011-06-02 04:24:34", is_admin: true>
PASS:
PEWPASS:
So the question is, the user DOES have a password, since it was allowed to be created, not to mention it printed it out after being created using the factory. Problem is, when I try to access the password via User.find(user.id) or operator.user, why is it that password is blank?
If it helps, this is for authlogic

It seems that I was trying to access a protected attribute of authlogic, that's why I couldn't access the password even if I tried getting the user object again. My solution was to use a global variable instead (#user) so I could access it in other steps.

Does this work?
Factory.define :admin, :class => :user do |f|

Related

put :update in rspec not updating attributes

I am trying to test the update action of a devise Model in my app.
My factories.rb file:
FactoryGirl.define do
factory :student do
first_name "John"
last_name "Doe"
sequence(:email) { |n| "#{first_name}.#{last_name}#{n}#example.com".downcase }
city "Dhaka"
area "Mirpur"
zip 1216
full_address "Mirpur, Dhaka"
password "password"
password_confirmation "password"
confirmed_at Date.today
end
end
The rspec file:
require 'rails_helper'
RSpec.describe Students::RegistrationsController do
context "Student logged in" do
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:student]
#student = FactoryGirl.create(:student)
#updated_attributes = { :first_name => "New", :last_name => "Name" }
sign_in #student
put :update, :id => #student.id, :student => #updated_attributes
#student.reload
end
it { expect(#student.first_name).to eql "New" }
it { expect(#student.last_name).to eql "Name" }
end
end
I expected the tests to pass. But they are failing. The failing messages look like this:
Failure/Error: it { expect(#student.first_name).to eql "New" }
expected: "New"
got: "John"
(compared using eql?)
Failure/Error: it { expect(#student.last_name).to eql "Name" }
expected: "Name"
got: "Doe"
(compared using eql?)
So, basically, the attributes are not getting updated. What do i need to do to make rspec update the attributes and make the tests green?
I forgot to include the :current_password attribute in the #updated_attribute hash. Devise requires you to enter your current password in the edit form. So, #updated_attribute should be,
#updated_attributes = FactoryGirl.attributes_for(:student, :first_name => "New", :last_name => "Name", :current_password => "password" )

Any way to DRY this FactoryGirl example?

I have the following factory:
FactoryGirl.define do
factory :user do
name 'Name'
password 'password'
email 'email#example.com'
end
end
I have the following code in before block (I am creating all possible variations of email-some_boolean_flag pairs where email can take '' and default value and some_boolean_flag can be false/nil or true):
FactoryGirl.create(:user, email: '', some_boolean_flag: false)
FactoryGirl.create(:user, email: '', some_boolean_flag: true)
FactoryGirl.create(:user, some_boolean_flag: nil)
FactoryGirl.create(:user, some_boolean_flag: true)
How can I DRY it? Is there any way in FactoryGirl to create a list of objects but with specific attributes being different and without repeating same line over and over? Thanks!
Factory:
FactoryGirl.define do
factory :user do
name 'Name'
password 'password'
email 'email#example.com'
factory :boolean_user
some_boolean_flag true
end
end
end
Test
['', 'email#example.com'].each do |email|
FactoryGirl.create(:user, email: email)
FactoryGirl.create(:boolean_user, email: email)
end
A note here, I am purposefully going with restating the 'email#example.com' because I like my factories to be what's needed to pass validations. I don't like to depend on the contents of a factory for my test to pass. I will always specifically call the data I need.

FactoryGirl: Change an attribute after FactoryGirl.create is called

In my user model, all users are assigned the role of user in a before_create callback. So I'm having a lot of trouble creating an admin user to use in some tests. Here is what I've tried, which is not working:
require 'spec_helper'
describe "Exercises" do
describe "GET /Exercises" do
it "gives the expected status code." do
sign_in_as_valid_user
#user.role = 'admin'
get exercises_path
response.status.should be(200)
end
for completeness, here is the method that is called:
module ValidUserRequestHelper
def sign_in_as_valid_user
FactoryGirl.create :program
#user ||= FactoryGirl.create :user
post_via_redirect user_session_path, 'user[email]' => #user.email, 'user[password]' => #user.password
end
end
and the factory:
FactoryGirl.define do
sequence :email do |n|
"test#{n}#vitogo.com"
end
factory :user do
email
password '12345678'
password_confirmation '12345678'
goal_id 1
experience_level_id 1
gender 'Female'
end
end
I'm just trying to change the role in the specific tests where it matters.
Any ideas how to do this? It's been driving me crazy. Thanks in advance!
I then edited my users Factory to create an Admin Factory that inherited from my User Factory, then assigned the admin role in an after(:create) callback like this:
factory :user do
email
password '12345678'
password_confirmation '12345678'
gender 'Male'
factory :admin do
after(:create) { |user| user.role = 'admin'; user.save }
end
end
Try wrapping the #user in a method, something like this in the ValidUserRequestHelper
def current_user
#user
end
Then calling current_user.role = 'admin' in your specs

Am I using factories correctly?

This is my current testing setup:
# spec/factories.rb
require 'factory_girl'
FactoryGirl.define do
# Roles
factory :user_role, :class => Role do
name 'User'
end
# Users
factory :user, :class => User do
sequence(:email) {|n| "email#{n}#example.com" }
password 'password'
password_confirmation 'password'
name 'Yuri Userington'
roles { |a| [a.association(:user_role)] }
end
# Instruments
factory :instrument, :class => Instrument do
title "Doobie Doo Instrument Title"
is_valid true
association :user, :factory => :user
end
# Sequences
sequence :email do
"email#{n}#factory.com"
end
end
# spec/controllers/instruments_controller_spec.rb
require 'spec_helper'
describe InstrumentsController do
before (:each) do
#instrument = FactoryGirl.create(:instrument)
#attr = FactoryGirl.attributes_for(:instrument)
#user = FactoryGirl.create(:user)
end
describe "GET index" do
it "assigns all instruments as #instruments" do
instrument = Instrument.new(#attr)
instrument.user = #user
instrument.save!
get :index
assigns(:instruments).should eq([instrument])
end
end
end
The result is that when i run my tests, i get the following errors in my output:
Failures:
1) InstrumentsController GET index assigns all instruments as #instruments
Failure/Error: #instrument = FactoryGirl.create(:instrument)
ActiveRecord::RecordNotFound:
Couldn't find Role with id=2
# ./app/models/user.rb:21:in `assign_role_after_sign_up'
# ./spec/controllers/instruments_controller_spec.rb:24:in `block (2 levels) in <top (required)>'
Based on that it seems like the roles association call in my :user factory is NOT being called -- what am i doing wrong here? Am i using this in a completely wrong way?
thank you!!
There is much to say here. Compare your code with the following to see how many lines or words were removed.
FactoryGirl.define do
# Sequences
sequence :email do |n|
"email#{n}#factory.com"
end
# Roles
factory :user_role, :class => Role do
name 'User'
end
# Users
factory :user do
email
password 'password'
password_confirmation 'password'
name 'Yuri Userington'
roles { |user| [Factory(:user_role)] } #many to many
end
# Instruments
factory :instrument, :class => Instrument do
title "Doobie Doo Instrument Title"
is_valid true
association :user #one-to-one or one-to-many
end
end
And in your tests:
describe InstrumentsController do
before (:each) do
#user = Factory(:user)
end
describe "GET index" do
it "assigns all instruments as #instruments" do
instrument = Factory(:instrument, :user => #user)
get :index
assigns(:instruments).should eq([instrument])
end
end
end
Moreover:
I personally prefer testing controller with mocks and stubs
I use let instead of instance variables and before_filter
I had a similar issues and I used a callback to assign roles like this:
Factory.define :user_with_admin_role, :parent => :user do |user|
user.after_create {|instance| instance.roles << Factory(:admin_role) }
end
So I think you should be able to do something akin to that:
# Users
factory :user, :class => User do
sequence(:email) {|n| "email#{n}#example.com" }
password 'password'
password_confirmation 'password'
name 'Yuri Userington'
after_create {|user| user.roles << Factory(:user_role) }
end
That is completely untested, so you may need to tweak things around.

rails factory girl getting "Email has already been taken"

This is my factory girl code, and every time I try to generate a review, it's telling me that "Email has already been taken", i've reset my databases, set the transition in spec_helper to true, but still haven't solved the problem. I'm new to this, am I using the association wrong? Thanks!
Factory.define :user do |user|
user.name "Testing User"
user.email "test#example.com"
user.password "foobar"
user.password_confirmation "foobar"
end
Factory.define :course do |course|
course.title "course"
course.link "www.umn.edu"
course.sections 21
course.description "test course description"
course.association :user
end
Factory.define :review do |review|
review.title "Test Review"
review.content "Test review content"
review.association :user
review.association :course
end
I know this is a pretty old question, but the accepted answer is out of date, so I figured I should post the new way of doing this.
FactoryGirl.define do
sequence :email do |n|
"email#{n}#factory.com"
end
factory :user do
email
password "foobar"
password_confirmation "foobar"
end
end
Source: Documentation
It's quite a bit simpler, which is nice.
You need to use a sequence to prevent the creation of user objects with the same email, since you must have a validation for the uniqueness of emails in your User model.
Factory.sequence :email do |n|
“test#{n}#example.com”
end
Factory.define :user do |user|
user.name "Testing User"
user.email { Factory.next(:email) }
user.password "foobar"
user.password_confirmation "foobar"
end
You can read more in the Factory Girl documentation.
In addition to the above answers you could add gem 'faker' to your Gemfile and it will provide unique emails.
FactoryGirl.define do
factory :admin do
association :band
email { Faker::Internet.email }
password "asdfasdf"
password_confirmation "asdfasdf"
end
end
sequence gives really unique email and Faker gives random password.
FactoryGirl.define do
sequence :email do |n|
"user#{n}#test.com"
end
factory :user do
email
password { Faker::Internet.password(min_length: 8, max_length:20) }
password_confirmation { "#{password}" }
end
end
For some reason the password_confirmation field wasn't working for me. What worked was this:
FactoryBot.define do
sequence :email do |n|
"user#{n}#test.com"
end
factory :user do
email
password { Faker::Internet.password(min_length: 8, max_length:20) }
confirmed_at { Time.current } # <---- This worked for me
end
end
Note that if you're not using Faker, you can have something as simple as `password { "password" } instead of that line.

Resources