put :update in rspec not updating attributes - ruby-on-rails

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" )

Related

testing updating a record in a request spec

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.

Devise Rspec registration controller test failing on update as if it was trying to confirm email address

I have the following route:
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks",
:registrations => 'users/registrations',
:sessions => "users/sessions" }
and the following controller test (registrations_controller_spec.rb):
require File.dirname(__FILE__) + '/../spec_helper'
describe Users::RegistrationsController do
include Devise::TestHelpers
fixtures :all
render_views
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
end
describe "POST 'create'" do
describe "success" do
before(:each) do
#attr = { :email => "user#example.com",
:password => "foobar01", :password_confirmation => "foobar01", :display_name => "New User" }
end
it "should create a user" do
lambda do
post :create, :user => #attr
response.should redirect_to(root_path)
end.should change(User, :count).by(1)
end
end
end
describe "PUT 'update'" do
before(:each) do
#user = FactoryGirl.create(:user)
#user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
sign_in #user
end
describe "Success" do
it "should change the user's display name" do
#attr = { :email => #user.email, :display_name => "Test", :current_password => #user.password }
put :update, :id => #user, :user => #attr
puts #user.errors.messages
#user.display_name.should == #attr[:display_name]
end
end
end
end
Now, when I run rspec spec I get (what I think) are weird results:
The "should create a user" test passes. The user count has increased by 1.
However, my "should change the user's display name" fails as follows:
1) Users::RegistrationsController PUT 'update' Success should change the user's display name
Failure/Error: #user.display_name.should == #attr[:display_name]
expected: "Test"
got: "Boyd" (using ==)
And the weird bit is that my statement:
puts #user.errors.messages
Renders the following message:
{:email=>["was already confirmed, please try signing in"]}
What's going on? The user is signed in! That's proved by the fact that the Rspec error returned the display_name of "Boyd". And why is it displaying a message looks as though it's related with account confirmation as opposed to updating a user's details?
Any help would be greatly appreciated!
This works. Thanks holtkampw for seeing what I wasn't! I put some extra code in there just to double check and everything is well!
it "should change the user's display name" do
subject.current_user.should_not be_nil
#attr = { :email => #user.email, :display_name => "Test", :current_password => #user.password }
puts "Old display name: " + subject.current_user.display_name
put :update, :id => subject.current_user, :user => #attr
subject.current_user.reload
response.should redirect_to(root_path)
subject.current_user.display_name == #attr[:display_name]
puts "New display name: " + subject.current_user.display_name
end

"NoMethodError: undefined method `destroy' for nil:NilClass" when Unit Testing Rails Model

I am creating a Rails app (fairly new at this), and I have a Client model that I generated without using scaffolding. I have run 'rake db:migrate', and I am unit testing the model with 'rake test:units', however I am getting variations of the following runtime error in terminal for practically all of my factory testing.
test: Creating seven clients should show that all factories are properly created. (ClientTest):
NoMethodError: undefined method `destroy' for nil:NilClass
/Users/myUserName/Desktop/app_name/test/unit/client_test.rb:131:in `block (2 levels) in <class:ClientTest>'
I have been unable to figure out what the error is. I understand that it doesn't recognize the client class and thus it can't find the 'destroy' method for nil. However, I'm unsure of how to fix it.
Below is my model code, located in app_name/app/models/client.rb
class Client < ActiveRecord::Base
# Callbacks
before_save :reformat_phone
# Relationships
has_many :assignments
has_many :counselors, :through => :assignments
has_many :interventions, :through => :assignments
# Validations
validates_presence_of :first_name, :last_name, :gender, :address, :city, :state, :zip, :phone, :active
validates_inclusion_of :gender, :in => %w[male female], :message => "is not an option"
validates_inclusion_of :marital_status, :in => %w[single married separated divorced], :message => "is not an option"
validates_inclusion_of :state, :in => %w[PA OH WV], :message => "is not an option"
validates_format_of :zip, :with => /^\d{5}$/, :message => "should be five digits long"
validates_format_of :phone, :with => /^\(?\d{3}\)?[-. ]?\d{3}[-.]?\d{4}$/, :message => "should be 10 digits (area code needed) and delimited with dashes only"
# Scopes
scope :active, where('active = ?', true)
scope :inactive, where('active = ?', false)
scope :alphabetical, order('last_name, first_name')
scope :receiving_gov_assistance, where('gov_assistance = ?', true)
scope :not_receiving_gov_assistance, where('gov_assistance = ?', false)
scope :male, where('gender = ?', 'male')
scope :female, where('gender = ?', 'female')
scope :by_marital_status, lambda { |status| where("marital_status = ?", status) }
scope :by_ethnicity, lambda { |race| where("ethnicity = ?", race) }
scope :employed, where('is_employed = ?', true)
scope :unemployed, where('is_employed = ?', false)
scope :veteran, where('is_veteran = ?', true)
scope :assigned, where('current_assignment != ?', nil)
scope :unassigned, where('current_assignment = ?', nil)
# Other methods
def name
"#{last_name}, #{first_name}"
end
def proper_name
"#{first_name} #{last_name}"
end
def current_assignment
curr_assignment = self.assignments.select{|a| a.end_date.nil?}
# alternative method for finding current assignment is to use scope 'current' in assignments:
# curr_assignment = self.assignments.current # will also return an array of current assignments
return nil if curr_assignment.empty?
curr_assignment.first # return as a single object, not an array
end
# Misc Constants
GENDER_LIST = [['Male', 'male'],['Female', 'female']]
STATES_LIST = [['Ohio', 'OH'],['Pennsylvania', 'PA'],['West Virginia', 'WV']]
# Callback code
# -----------------------------
private
def reformat_phone
phone = self.phone.to_s # change to string in case input as all numbers
phone.gsub!(/[^0-9]/,"") # strip all non-digits
self.phone = phone # reset self.phone to new string
end
end
And here are the tests I've written, located in app_name/test/unit/client_test.rb
require 'test_helper'
class ClientTest < ActiveSupport::TestCase
# Test relationships
should have_many(:assignments)
should have_many(:deacons).through(:assignments)
should have_many(:interventions).through(:assignments)
# Test basic validations
should validate_presence_of(:last_name)
should validate_presence_of(:first_name)
should validate_presence_of(:gender)
should validate_presence_of(:address)
should validate_presence_of(:city)
should validate_presence_of(:state)
should validate_presence_of(:zip)
should validate_presence_of(:phone)
should validate_presence_of(:active)
# Identity-based tests
# tests for gender
should allow_value("male").for(:gender)
should allow_value("female").for(:gender)
should_not allow_value(nil).for(:gender)
should_not allow_value(1).for(:gender)
should_not allow_value("seahorse").for(:gender)
should_not allow_value("I believe gender is a societal construct.").for(:gender)
# tests for ethnicity
should allow_value("Asian").for(:ethnicity)
should allow_value("Black").for(:ethnicity)
should allow_value("Hispanic").for(:ethnicity)
should allow_value("Latino").for(:ethnicity)
should allow_value("Native American").for(:ethnicity)
should allow_value("White").for(:ethnicity)
should_not allow_value(nil).for(:ethnicity)
should_not allow_value(1).for(:ethnicity)
should_not allow_value(true).for(:ethnicity)
should_not allow_value(0.5).for(:ethnicity)
# tests for marital status
should allow_value("single").for(:marital_status)
should allow_value("married").for(:marital_status)
should allow_value("separated").for(:marital_status)
should allow_value("divorced").for(:marital_status)
should_not allow_value("White").for(:marital_status)
should_not allow_value(nil).for(:marital_status)
should_not allow_value(1).for(:marital_status)
should_not allow_value(true).for(:marital_status)
should_not allow_value("I believe marriage is a societal construct.").for(:marital_status)
# Contact-based Tests
# tests for address
should allow_value("123 Example Lane").for(:address)
should allow_value("456 Another Street").for(:address)
should_not allow_value(true).for(:address)
should_not allow_value(101).for(:address)
should_not allow_value(nil).for(:address)
# tests for zip
should allow_value("12345").for(:zip)
should_not allow_value("bad").for(:zip)
should_not allow_value("1234").for(:zip)
should_not allow_value("123456").for(:zip)
should_not allow_value("12345-6789").for(:zip)
# tests for state
should allow_value("OH").for(:state)
should allow_value("PA").for(:state)
should allow_value("WV").for(:state)
should_not allow_value("bad").for(:state)
should_not allow_value("NY").for(:state)
should_not allow_value(10).for(:state)
should_not allow_value("CA").for(:state)
# tests for phone
should allow_value("4122683259").for(:phone)
should allow_value("412-268-3259").for(:phone)
should allow_value("412.268.3259").for(:phone)
should allow_value("(412) 268-3259").for(:phone)
should_not allow_value("2683259").for(:phone)
should_not allow_value("14122683259").for(:phone)
should_not allow_value("4122683259x224").for(:phone)
should_not allow_value("800-EAT-FOOD").for(:phone)
should_not allow_value("412/268/3259").for(:phone)
should_not allow_value("412-2683-259").for(:phone)
# Assistance-based tests
# tests for gov_assistance
should allow_value(true).for(:gov_assistance)
should allow_value(false).for(:gov_assistance)
should_not allow_value(150).for(:gov_assistance)
should_not allow_value("Yes").for(:gov_assistance)
# tests for is_employed
should allow_value(true).for(:is_employed)
should allow_value(false).for(:is_employed)
should_not allow_value(30000).for(:is_employed)
should_not allow_value("Movie theater usher").for(:is_employed)
# tests for is_veteran
should allow_value(true).for(:is_veteran)
should allow_value(false).for(:is_veteran)
should_not allow_value(nil).for(:is_veteran)
should_not allow_value("Marines").for(:is_veteran)
# Establish context
# Testing other methods with a context
context "Creating seven clients" do
setup do
#dan = FactoryGirl.create(:client)
#barney = FactoryGirl.create(:client, :last_name => "Saha", :first_name => "Barney", :active => false, :ethnicity => "Indian" )
#ryan = FactoryGirl.create(:client, :last_name => "Black", :first_name => "Ryan", :phone => "412-867-5309", :ethnicity => "White", :gov_assistance => true )
#joe = FactoryGirl.create(:client, :last_name => "Oak", :first_name => "Joseph", :ethnicity => "Asian", :is_employed => false )
#mary = FactoryGirl.create(:client, :last_name => "Clute", :first_name => "Mary", :gender => "female", :ethnicity => "White" )
#jon = FactoryGirl.create(:client, :last_name => "Carreon", :first_name => "Jon", :is_veteran => true )
#meg = FactoryGirl.create(:client, :last_name => "Smith", :first_name => "Megan", :ethnicity => "White", :gender => "female", :is_employed => false)
end
# and provide a teardown method as well
teardown do
#dan.destroy
#barney.destroy
#ryan.destroy
#joe.destroy
#mary.destroy
#jon.destroy
#meg.destroy
end
# test one of each factory
should "show that all factories are properly created" do
assert_equal "Tabrizi", #dan.last_name
assert #ryan.active
assert #joe.active
assert_equal "Mary", #mary.first_name
assert #jon.active
assert #meg.active
deny #barney.active
end
# test the callback is working 'reformat_phone'
should "shows that Ryan's phone is stripped of non-digits" do
assert_equal "4128675309", #ryan.phone
end
# test the scope 'alphabetical'
should "shows that there are seven clients in in alphabetical order" do
assert_equal ["Black", "Carreon", "Clute", "Oak", "Saha", "Smith", "Tabrizi"], Client.alphabetical.map{|s| s.last_name}
end
# test the scope 'active'
should "shows that there are six active clients" do
assert_equal 2, Client.active.size
assert_equal ["Black", "Carreon", "Clute", "Oak", "Smith", "Tabrizi"], Client.active.alphabetical.map{|s| s.last_name}
end
# test the scope 'inactive'
should "shows that there is one inactive client" do
assert_equal 1, Client.inactive.size
assert_equal ["Saha"], Client.inactive.alphabetical.map{|s| s.last_name}
end
# test the scope 'receiving_gov_assistance'
should "shows that there is one client receiving government assistance" do
assert_equal 1, Client.receiving_gov_assistance.size
assert_equal ["Black"], Client.receiving_gov_assistance.alphabetical.map{|s| s.last_name}
end
# test the scope 'not_receiving_gov_assistance'
should "shows that there are six clients not receiving government assistance" do
assert_equal 6, Client.not_receiving_gov_assistance.size
assert_equal ["Carreon", "Clute", "Oak", "Saha", "Smith", "Tabrizi"], Client.not_receiving_gov_assistance.alphabetical.map{|s| s.last_name}
end
# test the scope 'male'
should "shows that there are five male clients" do
assert_equal 6, Client.male.size
assert_equal ["Black", "Carreon", "Oak", "Saha", "Tabrizi"], Client.male.alphabetical.map{|s| s.last_name}
end
# test the scope 'female'
should "shows that there are two female clients" do
assert_equal 2, Client.female.size
assert_equal ["Clute", "Smith"], Client.female.alphabetical.map{|s| s.last_name}
end
# test the scope 'employed'
should "shows that there are five employed clients" do
assert_equal 5, Client.employed.size
assert_equal ["Black", "Carreon", "Clute", "Saha", "Tabrizi"], Client.employed.alphabetical.map{|s| s.last_name}
end
# test the scope 'unemployed'
should "shows that there are two unemployed clients" do
assert_equal 2, Client.unemployed.size
assert_equal ["Oak", "Smith"], Client.unemployed.alphabetical.map{|s| s.last_name}
end
# test the scope 'veteran'
should "shows that there is one employed clients" do
assert_equal 1, Client.veteran.size
assert_equal ["Carreon"], Client.veteran.alphabetical.map{|s| s.last_name}
end
# test the method 'name' #DONE
should "shows name as last, first name" do
assert_equal "Tabrizi, Dan", #dan.name
end
# test the method 'proper_name' #DONE
should "shows proper name as first and last name" do
assert_equal "Dan Tabrizi", #dan.proper_name
end
end
end
Thank you very much for your help. If there are any other files that are necessary to determine the issue, please let me know. Sorry if this is very simple or too vague, I'm new to Rails development and I am using the terminology to the best of my knowledge
EDIT
Below is my client factory as it currently stands, in app_name/test/factories.rb
FactoryGirl.define do
factory :client do
last_name "Tabrizi"
first_name "Dan"
gender "male"
ethnicity "Hispanic"
marital_status "single"
address "123 Example Lane"
city "Anytown"
state "PA"
zip "12345"
phone { rand(10 ** 10).to_s.rjust(10,'0') }
gov_assistance false
is_employed true
is_veteran false
active true
end
end
Hope you perform tests against separate test database, so it is safe to do:
teardown do
Client.destroy_all
end
Like Simon suggested, try commenting out or removing your teardown. Usually in these situations teardown masks what the real error is, because it will compile all your code and try to initiate the destroy method which will fail because your factories will have returned nil. Since that happens it will return the last error that was documented, which is why your receiving that error.

Rails and Rspec - can't mass-assign protected attributes

I am creating a Rails application with TDD using rspec. I am getting an error I can't remove :
Failure/Error: invalid_user = User.new(#attr.merge("provider" => ""))
ActiveModel::MassAssignmentSecurity::Error:
Can't mass-assign protected attributes: uid, info
Here is my user spec :
user_spec.rb
require 'spec_helper'
describe User do
before(:each) do
#attr = {"provider" => "providerexample", "uid" => "uidexample", "info" => {"name" => "Example"}}
end
it "should create a new instance given valid attributes" do
user = User.create_with_omniauth(#attr)
end
it "should require a provider" do
invalid_user = User.new(#attr.merge("provider" => ""))
invalid_user.should_not be_valid
end
it "should require a uid" do
invalid_user = User.new(#attr.merge("uid" => ""))
invalid_user.should_not be_valid
end
end
And my user.rb
class User < ActiveRecord::Base
attr_accessible :name, :credits, :email, :provider
validates :name, :provider, :uid, :presence => true
def self.create_with_omniauth(auth)
create! do |user|
user.provider = auth["provider"]
user.uid = auth["uid"]
user.name = auth["info"]["name"]
end
end
end
If I debug the mass-assign error by adding uid and info to the attr_accessible, I still get the following error unknown attribute: info.
If you merge what you had as #attr with info, then it'll exist for the create_with_omniauth call, but not the regular create methods.
describe User do
let(:user_attributes) { {"provider" => "providerexample", "uid" => "uidexample"} }
it "should create a new instance given valid attributes" do
expect {
User.create_with_omniauth(user_attributes.merge({"info" => {"name" => "example"}))
}.to not_raise_error
end
end

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