Ruby On Rails 3 Tutorial Chap 8: Unit tests not passing - ruby-on-rails

I'm working through Hartl's book and I'm up to chapter 8. I've written some tests that I believe should be passing. I've quadruple checked my code against what's in the book, and double checked it against what's in the book's github repo, but I'm stumped. I'm getting the following errors from RSpec:
Failures:
1) UsersController POST 'create' should redirect to the user "show" page
Failure/Error: response.should redirect_to(user_path(assigns(:user)))
ActionController::RoutingError:
No route matches {:action=>"show", :controller=>"users", :id=>#<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil, encrypted_password: nil, salt: nil>}
# ./spec/controllers/users_controller_spec.rb:93:in `block (3 levels) in <top (required)>'
2) UsersController POST 'create' should have a welcome message
Failure/Error: flash[:success].should =~ /welcome to the sample app/i
expected: /welcome to the sample app/i
got: nil (using =~)
# ./spec/controllers/users_controller_spec.rb:98:in `block (3 levels) in <top (required)>'
Finished in 0.83875 seconds
46 examples, 2 failures
Like I said, I've checked the code again and again. Restarted spork, restarted rails server, ran without spork. I've checked it against the code in the book and in the github repo. I've even copy/pasted the spec and controller code in the github repo, but all to no avail.
I'm stumped. It's late and I need to crash. :)
Hopefully one of you guys can see something I'm not. Here's what I've got so far...
users_controller_spec.rb
require 'spec_helper'
describe UsersController do
render_views
# ...
describe "POST 'create'" do
# ...
describe 'success' do
before(:each) do
#attr = { :name => 'New User', :email => 'some-email#gmail.com', :password => 'foobar', :password_confirmation => 'foobar' }
end
it 'should create a new user' do
lambda do
post :create, :user => #attr
end.should change(User, :count).by(1)
end
end
it 'should redirect to the user "show" page' do
post :create, :user => #attr
response.should redirect_to(user_path(assigns(:user)))
end
it 'should have a welcome message' do
post :create, :user => #attr
flash[:success].should =~ /welcome to the sample app/i
end
end
end
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
#title = 'Sign up'
end
def show
#user = User.find params[:id]
#title = #user.name
end
def create
#user = User.new(params[:user])
if #user.save
flash[:success] = 'Welcome to the Sample App!'
redirect_to #user
else
#title = 'Sign up'
render 'new'
end
end
end
user.rb
class User < ActiveRecord::Base
# Virtual properties (don't exist in db)
attr_accessor :password
# Accessible properties
attr_accessible :name, :email, :password, :password_confirmation
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
before_save :encrypt_password
# Return true if the user's password matches the submitted password
def has_password?(submitted_password)
# Compare encrypted_password with the encrypted version of submitted_password
encrypted_password == encrypt(submitted_password)
end
# Static/Class methods
def self.authenticate(email, submitted_password)
user = find_by_email email
return nil if user.nil?
return user if user.has_password? submitted_password
end
# Private functionality.
# Anything after the 'private' pragma will be inaccessable from outside the class
private
def encrypt_password
self.salt = make_salt if new_record? # Using ActiveRecord goodness to make sure this only gets created once.
self.encrypted_password = encrypt(password)
end
def encrypt(string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
end
routes.rb
SampleApp::Application.routes.draw do
#get '/users/new'
resources :users
match '/signup' => 'users#new'
match '/about' => 'pages#about'
match '/contact' => 'pages#contact'
match '/help' => 'pages#help'
root :to => 'pages#home'
end
Thanks in advance. If you guys really want to dig through or if I've missed something in my post, here's my code.
I'm very new to rails, and absolutely love it so far. Any help will be greatly appreciated.

Look closely at your users_controller_spec "success" spec: when will it create #attr? Before each test, or just before the "should create a new user" test? You use it in all the "POST 'create'" tests...
Once you make that non-spec-specific your tests will pass.
(By the way, having the code up in git is handy, but only if the code you're posting is actually checked in, otherwise... not as much ;)

your
it "should redirect to the user show page"
and
it "should have a welcome message"
is outside of the
describe "success" do
loop

Related

Rspec controller post request to render different partials

I am trying to write a controller spec to test that the right partial is rendering after a post request.
Here is the controller method being posted to:
def lookup
#guest = Guest.where("mobile_number = ?", params[:lookup_mobile_phone_number]).first_or_initialize do |g|
g.mobile_number = params[:lookup_mobile_phone_number]
end
if #guest.new_record?
#visit = Visit.new(hotel_id: params[:hotel_id])
render partial: "guests/form"
else
#visit = Visit.new(guest_id: #guest.id, hotel_id: params[:hotel_id])
render partial: "visits/form"
end
end
Here is the spec/controllers/guests_controller_spec.rb I wrote that is failing:
RSpec.describe GuestsController, :type => :controller do
describe "#lookup" do
render_views
let!(:returning_guest) { create(:test_guest) }
context "when guest is already registered with hotel" do
it "renders visits/form" do
post :lookup, :guest => { :lookup_mobile_phone_number => "5553331212"}
expect(response).to render_template(:partial => 'visits/form')
end
end
end
end
Here is the factory I'm using for :test_guest
FactoryGirl.define do
factory :test_guest, :class => 'Guest' do
name 'Jack Guest'
mobile_number '5553331212'
end
end
This is the response I am getting when the test fails
1) GuestsController#lookup when guest is already registered with hotel renders visits/form
Failure/Error: expect(response).to render_template(:partial => 'visits/form')
expecting partial <visits/form> but action rendered <["shared/_hotel_agent_name", "_hotel_agent_name", "guests/_form", "_form"]>.
Expected {"shared/_hotel_agent_name"=>1, "_hotel_agent_name"=>1, "guests/_form"=>1, "_form"=>1} to include "visits/form".
# ./spec/controllers/guests_controller_spec.rb:16:in `block (4 levels) in <top (required)>'
I've been hacking away at this a for a few days now, trying different approaches found on here with no luck. Any help would be much appreciated :)
You send
post :lookup, :guest => { :lookup_mobile_phone_number => "5553331212"}
but in controller, you use
params[:lookup_mobile_phone_number]
not
params[:guest][:lookup_mobile_phone_number]
So to fix it, according to your controller, do
post :lookup, :lookup_mobile_phone_number => "5553331212"

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

post:create in rspec test giving 'count should have been changed by 1, but was changed by 0'

I've got a problem with testing a create method of a controller. My test:
describe "POST #create" do
it "creates a new admin_user" do
expect{
post :create, :admin_user => FactoryGirl.attributes_for(:admin_user)
}.to change(Admin::User,:count).by(1)
end
end
And the failed spec I'm getting:
1) Admin::UsersController logged in POST #create creates a new admin_user
Failure/Error: expect{
count should have been changed by 1, but was changed by 0
# ./spec/controllers/admin_users_controller_spec.rb:75:in `block (4 levels) in <top (required)>'
Here's my controller:
def create
#admin_user = Admin::User.new(params[:admin_user])
if #admin_user.save
render nothing: true
end
end
and a factory:
require "factory_girl"
require 'ffaker'
FactoryGirl.define do
factory :admin_user, class: "Admin::User" do |f|
f.name {Faker::Name.first_name}
f.email {Faker::Internet.email}
f.password "aaa"
end
end
and my model:
class Admin::User < ActiveRecord::Base
has_secure_password
validates :email, :uniqueness => true, :presence => true
validates_presence_of :password, :on => :create
end
I have no idea what might be wrong. I've searched the whole internet for it but didn't find the answer. Adding user from the rails console works just fine. Any help would be highly appreciated. Thanks in advance.
This line:
post :create, FactoryGirl.attributes_for(:admin_user)
Should be this:
post :create, :admin_user => FactoryGirl.attributes_for(:admin_user)
As an exercise, print out the params (p params or puts params.inspect) in your create action in your controller and you'll see the difference.
EDIT You should try 2 things:
Still print out the param to see if they make sense to you.
The problem might be that your params just aren't valid. Instead of using save, try using save! which will throw an error if any of your validations are wrong, and you'll see the error in your test output. You should handle the situation if the save fails anyways.

How do I get Devise session#create test to pass

Here is my test:
require 'test_helper'
class SessionsControllerTest < ActionController::TestCase
setup do
#request.env["devise.mapping"] = Devise.mappings[:user]
#u = Factory :user, :password => :mypass, :password_confirmation => :mypass
end
test 'log in page loads' do
get :new
assert :success
end
test 'log in with devise password' do
post :create, :user => {:email => #u.email, :password => 'mypass'}
ap session
end
end
gives this output, indicating that the sign in failed:
Loaded suite test/functional/sessions_controller_test
Started
.{
"action" => "create",
"locale" => "en",
"controller" => "sessions",
"user" => {
"password" => "mypass",
"email" => "458286#email.com"
}
}
{
"flash" => {
:alert => "Invalid email or password."
}
}
.
Finished in 0.49123 seconds.
This is my session controller:
#this is an extension of the devise controller for sessions
class SessionsController < Devise::SessionsController
before_filter :set_title_h1, :only => :new
before_filter :debug, :only => :create
before_filter :old_password_system_fix, :only => :create
private
def set_title_h1
#layout[:show_h1] = false
title 'Sign in Or Register'
end
def after_sign_in_path_for(resource)
#override Devise default sign in path /opt/local/lib/ruby/gems/1.8/gems/devise-1.1.2/lib/devise/controllers/helpers.rb
#edit_user_registration_path
'/en/main/index' #forces locale to be defined
end
def after_sign_out_path_for(resource)
#override Devise default sign out path /opt/local/lib/ruby/gems/1.8/gems/devise-1.1.2/lib/devise/controllers/helpers.rb
main_index_path
end
def old_password_system_fix
#purpose is to bring old users into the new system by setting their old password to the new format
require 'digest/md5'
email = params[:user][:email]
pw = params[:user][:password]
#get user
u = User.find_by_email email
return if u.nil?
#if they don't have a devise-style pw, authenticate with old
if u.encrypted_password.blank? && u.old_password.present?
#if [params pw] == md5 [old pw] then create devise-style pw & salt, store it, and let them through to devise auth action
if u.old_password == Digest::MD5.hexdigest(pw)
set_devise_style_pw(u, pw)
#if no match, give "invalid email or pw" message.
else
#flash[:notice] = "Sign in failed."
flash[:notice] = t 'devise.failure.invalid'
#render :new
redirect_to new_user_session_path
end
end
end
def debug
ap params
end
end
What am I missing and how can I test a new session via a functional test?
Turns out you have to use an integration test, not a functional test. Don't ask me why...

Trouble on rspecting a FactoryGirl object

I am using Ruby on Rails 3.0.10, RSpec 2 and FactoryGirl. I have the following scenario:
In the models/user_spec.rb file I have
describe User do
let(:user) { Factory(:user) }
it "should have a 'registered' authorization do
user.authorization.should == "registered"
end
end
In the factories/user.rb file I have
FactoryGirl.define do
factory :user, :class => User do |user|
user.authorization 'registered'
end
end
In the user.rb file I have:
class User < ActiveRecord::Base
DEFAULT_AUTHORIZATION = 'registered'
validates :authorization,
:inclusion => {
:in => Authorization.all.map(&:name),
:message => "authorization is not allowed"
},
:presence => true
before_validation :fill_user_create, :on => :create
private
def fill_user_create
self.authorization = Authorization::DEFAULT_AUTHORIZATION
end
end
When I run the rspec command I get the following error:
User should have a default 'registered' Authorization
Failure/Error: let(:user) { Factory(:user) }
ActiveRecord::RecordInvalid:
Validation failed: Users authorization is not allowed
What is exactly the problem and how can I solve that?
BTW: In the models/user_spec.rb file I can use something like the following
let(:user) { User.create }
and it will work, but I prefer to use the FactoryGirl gem. What do you advice about?
Could you try modifying your spec as below and check what the results are:
it "should have a 'registered' authorization" do
system_names = Authorization.all.map(&:system_name)
system_names.should have_at_least(1).item
system_names.should include('registered')
user.authorization.should == "registered"
end

Resources