Rspec validation test fails on 'put' update - ruby

I have this factory: #host = FactoryGirl.create(:host)
This test passes:
it 'should update and redirect to the show page' do
new_attr = #host.attributes.merge("hostname" => "New_name")
put 'update', id: #host, host: new_attr
response.should redirect_to(host_path(#host))
end¬
But this one fails:
it 'should fail on validation for empty hostname and redirect to edit' do
bad_attr = #host.attributes.merge("hostname" => "")
put 'update', id: #host, host: bad_attr
response.should redirect_to(edit_host_path(#host))
end
With this error:
1) HostsController POST update failure should redirect to edit
Failure/Error: response.should redirect_to(edit_host_path(#host))
Expected response to be a redirect to <http://test.host/hosts/1/edit> but was a redirect to <http://test.host/hosts/1>
# ./spec/controllers/hosts_controller_spec.rb:127:in `block (4 levels) in <top (required)>'
In the #update of my HostsController, host objects that fail validation with empty hostnames are supposed to redirect_to edit_host_path(#host). Here is my HostsController#update
def update
#host = Host.find(params[:id])
if #host.save
flash[:notice] = 'Host was successfully updated.'
redirect_to host_path(#host)
else¬
redirect_to edit_host_path(#host)
end
end
I've played with saving and updating attributes in the console and my validations work. So why this error? Any ideas? Rspec seems to be saying that my factory objects with bad attributes are passing validation, but my model validates fine. Thank you.

/me does a face-palm.
In HostsController#update, this:
#host = Host.find(params[:id])
#host.save
Should be this:
#host = Host.find(params[:id])
#host.update_attributes(params[:host])

Related

Ruby returns nil class in testing, working fine in dev

I'm trying to implement a login system using rails rather than an external gem and have been following the Michael Hartl tutorial most apparently pop their cherry with. So far the site itself is functioning fine, it's 2 tests to do with logging in I'm struggling with:
require 'test_helper'
class UsersLoginTest < ActionDispatch::IntegrationTest
def setup
#user = users(:michael)
end
test "login with invalid information" do
get login_path
assert_template 'sessions/new'
post login_path, params: { session: { email: "", password: "" } }
assert_template 'sessions/new'
assert_not flash.empty?
get root_path
assert flash.empty?
end
test "login with valid information" do
get login_path
post login_path, params: { session: { email: #user.email,
password: 'password' } }
assert_redirected_to #user
follow_redirect!
assert_template 'users/show'
assert_select "a[href=?]", login_path, count: 0
assert_select "a[href=?]", logout_path
assert_select "a[href=?]", user_path(#user)
end
end
My error messages are:
ERROR["test_login_with_invalid_information", UsersLoginTest, 2016-06-30 22:30:36 +0100]
test_login_with_invalid_information#UsersLoginTest (1467322236.17s)
NoMethodError: NoMethodError: undefined method `[]' for nil:NilClass
app/controllers/sessions_controller.rb:7:in `create'
test/integration/users_login_test.rb:12:in `block in <class:UsersLoginTest>'
app/controllers/sessions_controller.rb:7:in `create'
test/integration/users_login_test.rb:12:in `block in <class:UsersLoginTest>'
ERROR["test_login_with_valid_information", UsersLoginTest, 2016-06-30 22:30:36 +0100]
test_login_with_valid_information#UsersLoginTest (1467322236.18s)
NoMethodError: NoMethodError: undefined method `[]' for nil:NilClass
app/controllers/sessions_controller.rb:7:in `create'
test/integration/users_login_test.rb:21:in `block in <class:UsersLoginTest>'
app/controllers/sessions_controller.rb:7:in `create'
test/integration/users_login_test.rb:21:in `block in <class:UsersLoginTest>'
The error codes point to the following controller:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
redirect_to user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
end
end
I assumed at the start this was due to the sessions helper methods not being available, however the site itself runs fine in development mode and logging in is possible. Am I missing something from my sessions helper or the test file itself? My sessions helper:
module SessionsHelper
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
# Returns the current logged-in user (if any).
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
# Returns true if the user is logged in, false otherwise.
def logged_in?
!current_user.nil?
end
end
And finally my application controller:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
include SessionsHelper
end
Apologies for the wall of text and thanks in advance
I think if you change this
post login_path, params: { session: { email: "", password: "" } }
into this
post login_path, session: { email: "", password: "" }
in your first test, it will pass. Try the same strategy with the second if it does. If that fails, I recommend writing a raise params.inspect in you create method in the controller at the very beginning, running the test to inspect the params, and then determining the solution from what you have learned.

How can we test /user/show?id=xx with rspec?

The route in my system is:
user_show GET /user/show(.:format) user#show.
The controller code is:
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #user}
end
end
_spec.rb
describe 'GET #show' do
it 'should return success' do
get :show, id:#user.id
expect(response).to be_success
end
end
result is:
Failure/Error: expect(response).to be_success
expected success? to return true, got false
In the browser, when I type xxx/user/show, it get error.
ActiveRecord::RecordNotFound in UserController#show
But if I type xxx/user/show?id=31, it shows user with id=31!!
Thanks for #Alex Wayne, I add more information here: I check the routes.rb file:
get "user/show"
get "user/index"
get "user/delete"
get "user/edit"
post "user/update"
resource :users, :path => :user, :as => :user
I personal think my teammate should not write down "get user/show, get user/index...." based on Rails Routing. But I can't change their code. So,
anyone know how to test user/show?id=xxx ? Many thanks~!!!!

Obtaining a render Argument Error: What is the correct syntax or usage?

I am new to Ruby, Sinatra and Pusher so this is a basic question. I am attempting to authenticate a Pusher Client (using iOS demo code https://github.com/lukeredpath/libPusher). The server code below fails with error when the iOS client attempts to join a presence channel:
ArgumentError - wrong number of arguments (1 for 2):
/Users/waveocean/.rvm/gems/ruby-1.9.3-p327/gems/sinatra-1.3.3/lib/sinatra/base.rb:665:in `render'
web.rb:13:in `auth'
web.rb:26:in `block in <main>'
/Users/waveocean/.rvm/gems/ruby-1.9.3-p327/gems/sinatra-1.3.3/lib/sinatra/base.rb:1265:in `call'
... snipped for brevity ...
Here is the code:
require 'sinatra'
require 'pusher'
require 'thin'
Thin::HTTP_STATUS_CODES[403] = "FORBIDDEN"
Pusher.app_id = 'MY-APP-ID'
Pusher.key = 'MY-KEY'
Pusher.secret = 'MY-SECRET'
def auth
response = Pusher[params[:channel_name]].authenticate(params[:socket_id], {:user_id => 101})
render :json => response
end
use Rack::Auth::Basic, "Protected Area" do |username, password|
username == 'foo' && password == 'bar'
end
post '/presence/auth' do
if params[:channel_name] == 'presence-demo'
auth
else
# render :text => "Forbidden", :status => '403'
response.status = 403
end
end
Can someone provide a suggestion or correct usage of render?
Here's is what I discovered. render is associated with Rails, and not strictly Ruby. To respond to the Sinatra route, use the following in the auth method:
def auth
response = Pusher[params[:channel_name]].authenticate(params[:socket_id], {:user_id => 101})
[200, {"Content-Type" => "application/json"}, response.to_json]
end
As it turns out, the Pusher iOS project demo provides a Scripts/auth_server.rb file with the required implementation. It is documented with the installation instructions here: https://github.com/lukeredpath/libPusher/wiki/Adding-libPusher-to-your-project .

Ruby on Rails RSpec put method doesn't see signed in user

so I've been using Michael Hartl's tutorial for some time and I can say it's really useful but there's a problem and I gues it's not on the tutorial's part. So in chapter "9.2.2 Requiring the right user" ther's a test for checking that a user can access neither other user's edit page nor submit a direct PUT reauest.
describe "as wrong user" do
let(:user) { FactoryGirl.create(:user) }
let(:wrong_user) { FactoryGirl.create(:user, email: "wrong#example.com") }
before { sign_in user }
describe "visiting Users#edit page" do
before { visit edit_user_path(wrong_user) }
it { should_not have_selector('title', text: full_title('Edit user')) }
end
describe "submitting a PUT request to the Users#update action" do
before { put user_path(wrong_user) }
specify { response.should redirect_to(root_path) }
end
end
So long all seems right but the test fails:
1) Authentication authorization as wrong user submitting a PUT request to the Users#update action ←[31mFailure/Error:←[0m ←[31mspecify { response.should redirect_to(root_path }←[0m←[31mExpected response to be a redirect to <http://www.example.com/> but was a redirect to <http://www.example.com/signin>←[0m←[36m # ./spec/requests/authentication_pages_spec.rb:107:in `block (5 levels) in <top (required)>'←[0m
Here's the User controller:
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:index, :edit, :update]
before_filter :correct_user, only: [:edit, :update]
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
end
def edit
end
def update
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in #user
redirect_to #user
else
render 'edit'
end
end
private
def signed_in_user
unless signed_in?
puts "No user signed in"
store_location
redirect_to signin_path, notice: "Please sign in."
end
end
def correct_user
#user = User.find(params[:id])
puts "Incorrect user" unless current_user?(#user)
redirect_to(root_path) unless current_user?(#user)
end
end
So as you can see the problem is that when using RSpec put method, the test fails even before checking for the right user because it sees ther's no user signed in.
This is a small problem which can easily be omitted (incorrect user cannot make direct PUT request anyway) but it's a puzzle for me why doesn't it work correct and I can't get the answer for quite a time already.
It looks like the signed_in_user filter is redirecting back to the sign in page before the correct_user fires. That suggests that the user is not actually signed in correctly by the sign_in user call in the before block.
Have you defined sign_in in spec/support/utilities.rb?
include ApplicationHelper
def sign_in(user)
visit signin_path
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
# Sign in when not using Capybara as well.
cookies[:remember_token] = user.remember_token
end

Using RSpec2 to test a controller's show action

I have a fairly simple Rails 3 project where I've defined a custom route:
get 'factions/:name' => 'factions#show', :as => :factions
get 'factions' => 'factions#index'
... which when running rails s gives me the expected page (http://localhost:3000/factions/xyz is HTTP 200 with the app/views/factions/show.html.haml being displayed). However I've tried multiple different ways of expressing a spec that will work, below is my latest incarnation:
require 'spec_helper'
describe FactionsController do
render_views
describe "GET 'show'" do
before { get '/xyz' }
subject { controller }
it { should respond_with(:success) }
it { should render_template(:show) }
end
describe "GET 'index'" do
it "should be successful" do
get 'index'
response.should be_success
end
end
end
The GET 'index' spec passes without complaint but no matter what I do the GET 'show' specs cannot pass - even if they do succeed when I am browsing to them locally.
1) FactionsController GET 'show'
Failure/Error: before { get '/xyz' }
ActionController::RoutingError:
No route matches {:controller=>"factions", :action=>"/xyz"}
# ./spec/controllers/factions_controller_spec.rb:7:in `block (3 levels) in <top (required)>'
The action really should be show but my routes.rb configuration must be incorrect or something. What gives?
(Additional context: I am using bundle exec spork for speeding up my specs and I have restarted the spork server multiple times just to make sure I'm not completely insane.)
Change:
before { get '/xyz' }
To:
before { get :show, :name => 'xyz' }

Resources