Any way to DRY this FactoryGirl example? - ruby-on-rails

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.

Related

Why does my test return a nil class error on an attribute it shouldn't?

I am trying to write a test for my InvitationsController#Create.
This is a POST http action.
Basically what should happen is, once the post#create is first executed, the first thing that needs to do is we need to check to see if a User exists in the system for the email passed in via params[:email] on the Post request.
I am having a hard time wrapping my head around how I do this.
I will refactor later, but first I want to get the test functionality working.
This is what I have:
describe 'POST #create' do
context 'when invited user IS an existing user' do
before :each do
#users = [
attributes_for(:user),
attributes_for(:user),
attributes_for(:user)
]
end
it 'correctly finds User record of invited user' do
post :create, { email: #users.first[:email] }
expect(response).to include(#users.first[:email])
end
end
end
This is the error I get:
1) Users::InvitationsController POST #create when invited user IS an existing user correctly finds User record of invited user
Failure/Error: post :create, { email: #users.first[:email] }
NoMethodError:
undefined method `name' for nil:NilClass
##myapp/gems/devise-3.2.4/app/controllers/devise_controller.rb:22:in 'resource_name'
# #myapp/gems/devise_invitable-1.3.6/lib/devise_invitable/controllers/helpers.rb:18:in 'authenticate_inviter!'
# #myapp/gems/devise_invitable-1.3.6/app/controllers/devise/invitations_controller.rb:67:in 'current_inviter'
# #myapp/gems/devise_invitable-1.3.6/app/controllers/devise/invitations_controller.rb:71:in 'has_invitations_left?'
I am using FactoryGirl and it works perfectly, in the sense that it returns valid data for all the data-types. The issue here is how do I get RSpec to actually test for the functionality I need.
Edit 1
Added my :user factory:
FactoryGirl.define do
factory :user do
association :family_tree
first_name { Faker::Name.first_name }
last_name { Faker::Name.last_name }
email { Faker::Internet.email }
password "password123"
password_confirmation "password123"
bio { Faker::Lorem.paragraph }
invitation_relation { Faker::Lorem.word }
# required if the Devise Confirmable module is used
confirmed_at Time.now
gender 1
end
end
It seems you're using Devise which require you to be logged in before going to the next step. On your error, Devise cannot get the same of your inviter because he's not logged.
Your test should be like this:
describe 'POST #create' do
context 'when invited user IS an existing user' do
before :each do
#users = [
attributes_for(:user),
attributes_for(:user),
attributes_for(:user)
]
#another_user = FactoryGirl.create(:user_for_login)
sign_in #another_user
end
it 'correctly finds User record of invited user' do
post :create, { email: #users.first[:email] }
expect(response).to include(#users.first[:email])
end
end
end
Example for FactoryGirl model for Devise
factory :user_for_login, class: User do |u|
u.email 'admin#myawesomeapp.com'
u.password 'password'
u.password_confirmation 'password'
u.name "MyName"
end
Of course, you need to add as much data as your validators want.. Basically for Devise you need email, password and password_confirmation. In you case, it seems you also need name.

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

Clearance Unit test in User model?

So I have been racking my brain at this and maybe some of you might have a better idea on how to do proper unit test for this User model. My basic unit test looks like this.
test "should not save without name" do
user = User.new
user.email = "test#test.com"
user.password = "letmein"
assert !user.save
end
This test passes with this model.
class User < ActiveRecord::Base
include Clearance::User
validates :name, presence: true
has_and_belongs_to_many :contests
end
Is there a better way to do this in Clearance? It is nice the gem lets you create users like this on the fly by arbitrarily assigning email and password but I'm thinking maybe I shouldn't have to do this.
user = User.new(:email => "test#test.com", :password => "letmein")
and then,
assert !user.valid?
or
user.should_not be_valid
or
expect { user.save }.to change(User, :count).by(0)

Factory Girl failing Rspec validation tests

I've been trying to get a grasp on writing tests, but having a lot of trouble as the tests never seem to validate the way I want them to. In particular, I've been trying to use Factory Girl as opposed to fixtures - as suggested by a recent Railscasts and other advice I've seen on the net - due to the benefits it professes, and that hasn't worked out.
For example, here is a simple Rspec test for a user model, testing to make sure a username is present...
describe User do
it "should not be valid without a username" do
user = FactoryGirl.create(:user, :username => "", :password => "secret")
user.should_not be_valid
end
end
And my factories.rb file, if it helps...
FactoryGirl.define do
factory :user do
sequence(:username) { |n| "registered-#{n}" }
password "foobar"
end
end
When I run 'rake spec,' it tells me...
1) User should not be valid without a username
Failure/Error: user = FactoryGirl.create(:user, :username => "", :password => "secret")
ActiveRecord::RecordInvalid:
Validation failed: Username can't be blank
Ummm...that's the POINT. If I specified that the user should NOT be valid, shouldn't this test actually pass?
If I replace the Factory Girl line and set the user in the test with something like 'user = User.new(:username => "", :password => "secret")', to no surprise the test passes fine. So why is Factory Girl not working right?
You should use build like in the following:
user = Factory.build(:user, :username=>"foo")
Because using the method you're using will try to create a record. See docs for further information.

Factory girl problems accessing certain attributes

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|

Resources