testing updating a record in a request spec - ruby-on-rails

I'm trying to update a record in a request spec, but it's not updating. Doing it in real life works. Here is the spec:
describe "sessions" do
before do
#user = FactoryGirl.create(:user)
#api_key = FactoryGirl.create(:api_key)
end
it "is updated properly" do
put "/api/v1/users/#{#user.id}?user_email=#{#user.email}&auth_token=#{#user.authentication_token}", {user: {name: "New Name"}},{ "HTTP_AUTHORIZATION"=>"Token token=\"#{#api_key.access_token}\"" }
#user.name.should eq("New Name")
response.status.should be(201)
end
end
The above test fails with the error:
Failure/Error: #user.name.should eq("New Name")
expected: "New Name"
got: "nil"
(compared using ==)
Name is an optional parameter, so I just don't set it in the Factory. If I do set it the line says got: "Bill" for example.
and, for completeness, here are the factories:
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'
factory :admin do
after(:create) { |user| user.role = 'admin'; user.save }
end
end
end
FactoryGirl.define do
factory :api_key do
access_token "MyString"
end
end

You just need to reload #user after your PUT call, e.g. #user.reload.

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

Validations Cause Test To Fail

I have the following test:
test "should get create" do
sign_in(FactoryGirl.create(:user))
assert_difference('Inquery.count') do
post :create, FactoryGirl.build(:inquery)
end
assert_not_nil assigns(:inquery)
assert_response :redirect
end
and I keep getting:
2) Error:
test_should_get_create(InqueriesControllerTest):
ActiveRecord::RecordInvalid: Validation failed: Email has already been taken, License number has already been taken
What I don't understand is why I get this error in this particular test, when I have a very similar test:
test "should get create" do
sign_in(FactoryGirl.create(:user, admin: true))
assert_difference('Event.count') do
post :create, FactoryGirl.build(:event)
end
assert_not_nil assigns(:event)
assert_response :success
end
and this does just fine. The obvious difference is the admin: true line, but that has no effect as I suspected.
Added:
User_factory.rb
factory :user do
first_name "John"
last_name "Doe"
email "example#example.com"
password "foobar"
password_confirmation "foobar"
license_number '12345'
state 'AZ'
specialty 'Neurosurgery'
end
Your User is failing validations because your factory is setting up a new user for your test, but your database isn't being cleared in between. Change your factory to look like this, so that email and license_number are unique each time you create a User:
factory :user do
first_name "John"
last_name "Doe"
sequence(:email) { |n| "example#{n}#example.com" }
password "foobar"
password_confirmation "foobar"
sequence(:license_number) { |n| "12345#{n}" }
state 'AZ'
specialty 'Neurosurgery'
end

Object.save failed in spec data validation

Here is the failed spec code for create in customer controller:
describe CustomersController do
before(:each) do
#the following recognizes that there is a before filter without execution of it.
controller.should_receive(:require_signin)
controller.should_receive(:require_employee)
end
render_views
describe "'create' successful" do
before(:each) do
category = Factory(:category)
sales = Factory(:user)
#customer = Factory.attributes_for(:customer, :category1_id => category.id, :sales_id => sales.id)
session[:sales] = true
session[:user_id] = sales.id
session[:user_name] = sales.name
session[:page_step] = 1
session['page1'] = customers_path
end
it "should create one customer record" do
lambda do
post 'create', #customer
end.should change(Customer, :count).by(1)
end
it "should redirect to customers path" do
put 'create', #customer
flash[:notice].should_not be_nil
response.should redirect_to(customers_path)
end
end
end
The customer has both sales id and category id which belong to user and category table respectively.
Here is the spec failure error:
1) CustomersController GET customer page 'create' successful should create one customer record
Failure/Error: lambda do
count should have been changed by 1, but was changed by 0
# ./spec/controllers/customers_controller_spec.rb:37:in `block (4 levels) in <top (required)>'
2) CustomersController GET customer page 'create' successful should redirect to customers path
Failure/Error: flash[:notice].should_not be_nil
expected: not nil
got: nil
# ./spec/controllers/customers_controller_spec.rb:44:in `block (4 levels) in <top (required)>'
Here is the app code for create in customer controller:
def create
if session[:sales]
#customer = Customer.new(params[:customer], :as => :roles_new_update)
#customer.sales_id = session[:user_id]
if #customer.save
#message = "New customer #{params[:name]} was created. Please check it out"
#subject = "New customer #{params[:name]} was created BY {#session[:user_name]}"
UserMailer.notify_tl_dh_ch_ceo(#message, #subject, session[:user_id])
redirect_to session[('page' + session[:page_step].to_s).to_sym], :notice => 'Customer was created successfaully!'
else
render 'new', :notice => 'Customer was not saved!'
end
end
end
Here is the code in factories.rb:
Factory.define :customer do |c|
c.name "test customer"
c.short_name "test"
c.email "t#acom.com"
c.phone "12345678"
c.cell "1234567890"
c.active 1
c.category1_id 2
c.sales_id 1
c.address "1276 S. Highland Ave, Lombard, IL 67034"
c.contact "Jun C"
end
Factory.define :category do |c|
c.name "category name"
c.description "test category"
c.active true
end
Factory.define :user do |user|
user.name "Test User"
user.email "test#test.com"
user.password "password1"
user.password_confirmation "password1"
user.status "active"
user.user_type "employee"
end
It seems that the error was caused by #customer.save returning false and the code for "if #customer.save" was not executed. So the problem may be with the #customer generated by Factory which seems good to me. The code is executed without any problem when saving a customer.
Any suggestions? Thanks.
I would break this up into two specific tests. Right now you're unsure of two things:
is the customer is being told to save itself?
Is there a validation that is preventing customer from being saved?
The quickest path is to change #customer.save to #customer.save! and see if there are any exceptions raised (it will do so if a validation failed).
I recommend you split this up though. To test #1, in the controller spec:
it "should tell the customer to save itself when there is a session[:sales]" do
session[:sales] = true
customer_mock = double(:customer)
customer_mock.should_receive(:sales_id=)
customer_mock.should_receive(:save).and_return(:true)
Customer.stub(:new => cutomer_mock)
post 'create'
end
Then in your customer_spec, test out:
it "should be valid with factory specs" do
customer = Customer.new(Factory.attributes_for(:customer))
customer.should be_valid
end
post :create, :customer => #customer
solves the problem with above.

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.

Resources