My rspec tests are failing when I try to use .reload to test the Update method.
The error
Admin::CompaniesController PUT update valid attributes changes #company's attributes
Failure/Error: #company.reload
TypeError:
no implicit conversion of nil into Hash
# ./spec/controllers/admin/admin_companies_controller_spec.rb:81:in `block (4 levels) in <top (required)>'
The spec
# spec/controllers/admin_companies_controller_spec.rb
describe 'PUT update' do
before :each do
#company = create(:company, name: "Rockstars", phone: "3035551212")
end
context "valid attributes" do
it "changes #company's attributes" do
put :update, id: #company, company: attributes_for(:company, name: "Fishstars")
#company.reload #line 81
expect( #company.name ).to eq("Fishstars")
end
end
end
I have also tried (1) using let!(), (same error)
describe 'PUT update' do
let!(:company) { create(:company, name: "Rockstars", phone: "303555121d") }
context "valid attributes" do
it "changes company's attributes" do
put :update, id: company, company: attributes_for(:company, name: "Fishstars")
company.reload #line 81
expect( company.name ).to eq("Fishstars")
end
end
end
I have also tried (2) (same error)
before :each do
#company = create(:company, name: "Rockstars", phone: "3035551212")
end
it "changes #company's attributes" do
#attr = { name: "Fishstars" }
put :update, id: #company.id, :company => #attr
#company.reload
expect( #company.name ).to eq("Fishstars")
end
My first thought was that the variable #company was nil, but if I comment out line 81 (indicated) the test fails as expected.
Update
inserting 'byebug' just before line 81 allows me to see what #company looks like:
(byebug) #company
#<Company id: 1, name: "Rockstars", hq_address: "MyString", hq_city: "MyString", hq_state: "MyString", country: "MyString", description: "Description 1", phone: "3035551212", email: "Example2email#gmail.com", owner_id: 2, contact_name: "MyString", created_at: "2014-07-11 19:02:24", updated_at: "2014-07-11 19:02:24", active: true, logo_file_name: nil, logo_content_type: nil, logo_file_size: nil, logo_updated_at: nil, slug: "rockstars", refund_policy: "Refund policy">
(byebug) #company.reload
TypeError Exception: no implicit conversion of nil into Hash
nil
I wasn't able to figure out why .reload wasn't working, but I replaced it with a find_by_id call and my tests are working properly now:
# new code - effectively tests the update method
#company = Company.find_by_id(#company)
# original code - causes errors
#company.reload #line 81
Related
My RSpec for ResidenceInformations Controller is resulting in failure when I run 'rake spec'. My Controller code is as follows
class ResidenceInformationsController < ApplicationController
def index
end
def show
render partial: 'subregion_select'
end
def new
#residence_info = ResidenceInformation.new
#saved_residence_info = ResidenceInformation.where('applicant_id=?', current_applicant.id)
end
def create
#residence_info = ResidenceInformation.new(residence_informations_params)
#residence_info.state = params[:order][:state]
#residence_info.applicant_id = current_applicant.id
##residence_info.residence_type_id = params[:residence_type][:residence_type_id]
#residence_info.save!
if cookies[:residence_next] == 'true' && cookies[:add_another] == 'false'
redirect_to new_employment_information_path
elsif cookies[:residence_next] == 'false' && cookies[:add_another] == 'true'
#cookies[:add_another] = 'false'
redirect_to request.referer
elsif cookies[:residence_next] == 'false' && cookies[:add_another] == 'false'
redirect_to request.referer
end
end
def edit
#residence_info = ResidenceInformation.find(params[:id])
end
def update
end
def destroy
end
def subregion_options
render partial: 'subregion_select'
end
private
def residence_informations_params
params.require(:residence_information).permit(:address1, :address2, :country, :state, :city, :zip, :reason_for_moving, :resident_since, :resident_upto, :was_notice_given, :monthly_rent, :residence_type_id, :applicant_id)
end
end
And This is my spec file for the above mentioned controller
require 'rails_helper'
RSpec.describe ResidenceInformationsController, :type => :controller do
describe 'GET #new' do
login_applicant
it 'should have a current_applicant' do
subject.current_applicant.should_not be_nil
end
it 'assigns a new ResidenceInformation to #residenceinfo' do
#residenceinfo = FactoryGirl.create(:residence_information, address1: 'dsada', country: 'india', state: 'wb', city: 'kolkata', zip: '700091', reason_for_moving: 'none', resident_since: '2015-01-05', was_notice_given: 'true', residence_type_id: 1, applicant_id: 13)
#puts"****************#{residenceinfo.inspect}***********************"
get :new
assigns(:residenceinfo).should be_a_new(ResidenceInformation)
end
it 'renders the :new template' do
get :new
response.should render_template :new
end
end
describe 'POST create' do
login_applicant
context 'with valid attributes' do
it 'should have a current_applicant' do
subject.current_applicant.should_not be_nil
end
it 'create a new ResidenceInformation' do
expect{
post :create, residenceinformation: FactoryGirl.create(:residence_information, address1: 'dsada', country: 'india', state: 'wb', city: 'kolkata', zip: '700091', reason_for_moving: 'none', resident_since: '2015-01-05', was_notice_given: 'true', residence_type_id: 1, applicant_id: 13)
}.to change(ResidenceInformation, :count).by(1)
end
end
end
end
I am getting the following error when running this spec file
Failure/Error: assigns(:residenceinfo).should be_a_new(ResidenceInformation)
expected nil to be a new ResidenceInformation(id: integer, address1: string, address2: string, country: string, state: string, city: string, zip: string, monthly_rent: integer, reason_for_moving: string, resident_since: date, resident_upto: date, was_notice_given: boolean, created_at: datetime, updated_at: datetime, residence_type_id: integer, applicant_id: integer)
# ./spec/controllers/residence_informations_controller_spec.rb:17:in `block (3 levels) in <top (required)>'
As I am completely new to RSpec , so I would appreciate any kind of help I get from you people. Thanks in advance
EDIT
Sorry , I had given the wrong spec file. now given the right one
Your instance variable is named #residence_info and you are checking :residenceinfo. Try it with the underscore:
assigns(:residence_info).should be_a_new(ResidenceInformation)
Tested in browser and works fine. Test error says "expected result to have changed from 0 to 1, but did not change". Is this a factory issue or rspec issue? Why is it not changing?
Error:
Failures:
1) ShortLinksController Short links controller Clicking a short link increments the click counter by 1
Failure/Error: expect{ get :url_dispatch, { id: short_link.short_link } }.to change{short_link.click_counter}.from(0).to(1)
expected result to have changed from 0 to 1, but did not change
# ./spec/controllers/short_links_controller_spec.rb:34:in `block (4 levels) in <top (required)>'
Rspec:
it "increments the click counter by 1" do
short_link = create(:short_link)
expect{ get :url_dispatch, { id: short_link.short_link } }.to change{short_link.click_counter}.from(0).to(1)
end
Controller:
def url_dispatch
id = params[:id]
record = ShortLink.where(["short_link = ?", id]).first
if record.update(click_counter: record.click_counter + 1)
redirect_to record.redirect_to
else
render '/not_found'
end
end
Factory:
FactoryGirl.define do
factory :short_link do
redirect_to "http://google.com"
title "This is the google page"
short_link "xGh7u"
click_counter 0
owner Owner.create!(first_name: "Bob", last_name: "Diller", email: "bdiller#example.com")
end
end
per Fab's request, here is how I'm currently working around the issue.
context 'save invocations' do
before(:each) do
#org = create(:organization)
user = create(:user, organization: #org, is_admin: true)
sign_in user
end
it 'valid scenario' do
user2 = create(:user, organization: #org, is_admin: false)
put :update, id: user2, user: { is_admin: true }
user2.reload
expect(response).to have_http_status(204)
expect(user2.is_admin).to eq true
end
end
Here I'm calling user2.reload in order to get the updated attributes from the user2 factory.
I don't know why the expect{} syntax doesn't work for factories but you could refactor your code like this:
it "increments the click counter by 1" do
short_link = create(:short_link)
count = short_link.click_counter
get :url_dispatch, { id: short_link.short_link }
short_link.reload
expect(short_link.click_counter).to eq count + 1
end
Again I'm not saying this is best practice, I just couldn't find anything in the FactoryGirl documentation regarding RSpec 3 expect syntax in controllers that update attributes.
I can't figure out what I'm doing wrong in my model spec (user_spec.rb). I'm attempting to check that the authentication method works with a valid password, but something is going wrong in the User.find_by part of the spec I think since it returns nil. I've checked that it responds to :authenticate, :password, and :password_confirmation, which passes (I'll omit that in the code below for the sake of readability).
Here's the failure I'm getting when running the spec:
Failures:
1) User return value of authenticate method with valid password should eq #<User id: 136, fullname: "Sherwood Hickle", email: "jewell.brekke#stokes.net", created_at: "2013-11-06 23:04:35", updated_at: "2013-11-06 23:04:35", password_digest: "$2a$04$O5V2X9sZrl/u2T9W25c3Pu/PU6XaIvtZSIB39Efkid6a...">
Failure/Error: it { should eq found_user.authenticate(user.password) }
expected: #<User id: 136, fullname: "Sherwood Hickle", email: "jewell.brekke#stokes.net", created_at: "2013-11-06 23:04:35", updated_at: "2013-11-06 23:04:35", password_digest: "$2a$04$O5V2X9sZrl/u2T9W25c3Pu/PU6XaIvtZSIB39Efkid6a...">
got: #<User id: nil, fullname: nil, email: nil, created_at: nil, updated_at: nil, password_digest: nil>
(compared using ==)
Diff:
## -1,2 +1,2 ##
-#<User id: 136, fullname: "Sherwood Hickle", email: "jewell.brekke#stokes.net", created_at: "2013-11-06 23:04:35", updated_at: "2013-11-06 23:04:35", password_digest: "$2a$04$O5V2X9sZrl/u2T9W25c3Pu/PU6XaIvtZSIB39Efkid6a...">
+#<User id: nil, fullname: nil, email: nil, created_at: nil, updated_at: nil, password_digest: nil>
# ./spec/models/user_spec.rb:41:in `block (4 levels) in <top (required)>'
user_spec.rb:
require 'spec_helper'
describe User do
describe "return value of authenticate method" do
user = FactoryGirl.create(:user)
let(:found_user) { User.find_by(email: user.email) }
describe "with valid password" do
it { should eq found_user.authenticate(user.password) }
end
describe "with invalid password" do
let(:user_for_invalid_password) { found_user.authenticate("invalid") }
it { should_not eq user_for_invalid_password }
specify { expect(user_for_invalid_password).to be_false }
end
end
end
And I'm also using FactoryGirl to generate the username and email addresses but I don't think it has anything to do with the failure:
require 'faker'
FactoryGirl.define do
factory :user do |u|
u.fullname { Faker::Name.name }
u.email { Faker::Internet.email }
u.password { "foobar" }
u.password_confirmation { "foobar" }
end
end
The problem is the way the spec is set up. You're using an implicit subject which, for this spec, will be User.new. That's why you're seeing a User object with all nil attributes.
Your it clauses should read more like
it { found_user.authenticate(user.password).should eq user }
and
it { user_for_invalid_password.should be_nil }
which actually test the behavior.
UPDATE:
With the introduction in RSpec 3.x of expect syntax as the preferred syntax, this would now be:
it { expect(found_user.authenticate(user.password)).to eq user }
and
it { expect(user_for_invalid_password).to be_nil }
I am trying to test a nested route however I am getting a undefined method customer for nil:NilClass. I have got the following RSpec test:
let(:valid_attributes) do
{alert_type: 'Error', subject: 'Triple-buffered responsive system engine',
state: 'Allocated', run_date: '2013-09-30 13:56:58', priority: 1, examined_on: '2013-09-30 13:56:58'
}
end
let(:valid_card_attributes) do
{name_on_card: 'Botsford', expiration_date: '2013-09-24',
expiration_month: '2013-09-24', valid_year: '2013-09-24', valid_month: '2013-09-24',
card_number: '2456-6996-2785-3769', bin: '8384-0294'
}
end
let(:valid_violation_attributes) do
{internal_code: 'Subsche', rule_priority: '96',
rule_id: '10', account_id: '10',
authorisation_id: '356'
}
end
let(:valid_customer_attr) do
{ first_name: 'CustomerString', last_name: 'CustomerString',
address1: 'CustomerString' , address2: 'CustomerString', post_code: 'CustomerString',
telephone: 'CustomerString', country: 'CustomerString', member_id: 1,
merchant_id: 1
}
end
let(:valid_session) { {} }
context 'JSON' do
describe 'GET show' do
it "delivers an alert with ID in JSON when a user requests '/api/alerts/id'" do
alert = Alert.create! valid_attributes
get :show, {:id => alert.to_param}, :format => :json
assigns(:alert).should eq(alert)
end
end
describe 'GET customer'
it 'delivers an alert with a customer and associated card' do
alert = Alert.create! valid_attributes
customer = Customer.create! valid_customer_attr
card = Card.create! valid_card_attributes.merge(customer_id: customer.id)
Violation.create! valid_violation_attributes.merge(alert_id: alert.id, customer_id: customer.id)
get :customer, {:id => alert.to_param}, :format => :json
assigns(:alert).customer.cards.first.should eq(card)
end
end
end
The console.log of the error that I am getting back is:
NoMethodError: undefined method `customer' for nil:NilClass
./app/models/alert.rb:10:in `customer'
./app/controllers/alerts_controller.rb:22:in `customer'
./spec/controllers/alerts_controller_spec.rb:33:in `block (3 levels) in <top (required)>'
Doing violation.first.customer just simply returns the first violation and the associated customer.
Be interested if anyone can shed any light on this.
violations.first in your Alert class is nil.
You're not setting up any Violations for your Alert in your test, so #alert.violations will be an empty array. Calling first on an empty array is nil, and you can't call customer on nil.
You can get around this using try (eg. violations.first.try(:customer)), or more correctly to check for the presence of any violations (eg. violations.first.customer if violations.any?).
OK, here's my rspec code ...
before(:each) do
#attr = { :greeting => "Lorem ipsum", :recipient => #recipient }
end
it "should redirect to the home page" do
puts "spec: #attr = #{#attr}"
puts "spec: recipient = #{#attr[:recipient]}"
post :create, :card => #attr
response.should redirect_to(root_path)
end
Now the output from this is:
spec: #attr = {:greeting=>"Lorem ipsum", :recipient=>#<User id: 2, first_name: "Example", last_name: "User", email: "recipient#example.com", created_at: "2011-12-22 04:01:02", updated_at: "2011-12-22 04:01:02", encrypted_password: "2d1323ad5eb21fb5ae5e87dfa78a63b521c56833189cc049ee2...", salt: "2679fcc29a30e939541cb98cb65d1d508035fea0eff1136037a...", admin: false>}
spec: recipient = #<User:0xac5d80c>
So we can see that recipient is a User.
On the controller side, we see have ...
def create
puts "create: Params = #{params}"
#card = current_user.sent_cards.build(params[:card])
if #card.save
flash[:success] = "Card created!"
redirect_to root_path
else
render 'pages/home'
end
end
With a display of ...
create: Params = {"card"=>{"greeting"=>"Lorem ipsum", "recipient"=>"2"}, "controller"=>"cards", "action"=>"create"}
and I see an error of ...
1) CardsController POST 'create' success should create a card
Failure/Error: post :create, :card => #attr
ActiveRecord::AssociationTypeMismatch:
User(#90303150) expected, got String(#76171330)
# ./app/controllers/cards_controller.rb:7:in `create'
# ./spec/controllers/cards_controller_spec.rb:47:in `block (5 levels) in <top (required)>'
# ./spec/controllers/cards_controller_spec.rb:44:in `block (4 levels) in <top (required)>'
So ... how did the User object get changed into its id as a string? Any ideas?
You cannot pass an entire object as a parameter. Rails replaces the object with its id if it has one or else passes a string representation of the object i.e. #<User:0xac5d80c> for your case if it doesn't find the id.
So for your case, you should rename the :recipient parameter to :recipient_id. Then
#card = current_user.sent_cards.build(params[:card])
will create your card with the associated recipient as we have passed in the recipient_id.