Reputation: 559
So I've seen several questions on here somewhat similar to mine, but not quite close enough to help me figure out what my issue is...so any help on what i'm doing wrong would be appreciated: (I'm following Michael Hartl's tutorial so info on how i've deviated from his examples would be particularly helpful)...thx
I'm throwing the following 2 errors:
Failures:
1) Authentication authorization as wrong user submitting a PUT request to the Users#update action
Failure/Error: before { put user_path(wrong_user) }
NoMethodError:
undefined method `put' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_3::Nested_2::Nested_2:0x007f8943728688>
# ./spec/features/authentication_pages_spec.rb:78:in `block (5 levels) in <top (required)>'
2) Authentication authorization for non-signed-in users in the Users controller submitting to the update action
Failure/Error: before { put user_path(user) }
NoMethodError:
undefined method `put' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_3::Nested_1::Nested_1::Nested_2:0x007f8944a85ff0>
# ./spec/features/authentication_pages_spec.rb:61:in `block (6 levels) in <top (required)>'
Finished in 3.06 seconds
56 examples, 2 failures
Failed examples:
rspec ./spec/features/authentication_pages_spec.rb:79 # Authentication authorization as wrong user submitting a PUT request to the Users#update action
rspec ./spec/features/authentication_pages_spec.rb:62 # Authentication authorization for non- signed-in users in the Users controller submitting to the update action
My Code is as follows:
spec/features/authentication_pages_spec.rb
require 'spec_helper'
describe "Authentication" do
subject { page }
# FOR STATIC SIGN IN PAGE
describe "signin page" do
before { visit signin_path }
it { should have_selector('h1', text: 'Sign in') }
it { should have_title('Sign in') }
end
# FOR SIGNIN PROCESS
describe "signin" do
before { visit signin_path }
describe "with invalid information" do
before { click_button "Sign in" }
it { should have_title('Sign in') }
it { should have_error_message }
describe "after visiting another page" do
before { click_link "Home" }
it { should_not have_error_message }
end
end
describe "with valid information" do
let(:user) { FactoryGirl.create(:user) }
before { sign_in user }
it { should have_title(user.name) }
it { should have_link('Profile', href: user_path(user)) }
it { should have_link('Sign out', href: signout_path) }
it { should have_link('Settings', href: edit_user_path(user)) }
it { should_not have_link('Sign in', href: signin_path) }
describe "followed by signout" do
before { click_link "Sign out" }
it { should have_link('Sign in') }
end
end
end
# FOR AUTHORIZING WHICH ACCOUNTS CAN DO WHAT
describe "authorization" do
describe "for non-signed-in users" do
let(:user) { FactoryGirl.create(:user) }
describe "in the Users controller" do
describe "visiting the edit page" do
before { visit edit_user_path(user) }
it { should have_title('Sign in') }
end
describe "submitting to the update action" do
before { put user_path(user) }
specify { response.should redirect_to(signin_path) }
end
end
end
describe "as wrong user" do
let(:user) { FactoryGirl.create(:user) }
let(:wrong_user) { FactoryGirl.create(:user, email: "[email protected]") }
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
end
end
My spec/features/user_pages_spec.rb
require 'spec_helper'
describe "User pages" do
subject { page }
# TESTING STATIC PROFILE PAGE
describe "profile page" do
let(:user) { FactoryGirl.create(:user) } # Code to make a user variable
before { visit user_path(user) }
it { should have_selector('h1', text: user.name) }
it { should have_title(user.name) }
end
# TESTING STATIC SIGNUP PAGE
describe "signup page" do
before { visit signup_path }
it { should have_selector('h1', text: 'Sign up') }
it { should have_title("Sign up") }
end
# TESTING SIGN UP PROCESS
describe "signup" do
before { visit signup_path }
let(:submit) { "Create my account" }
describe "with invalid information" do
it "should not create a user" do
expect { click_button submit }.not_to change(User, :count)
end
describe "after submission" do
before { click_button submit }
it { should have_title('Sign up') }
it { should have_content('error') }
end
end
describe "with valid information" do
before do
fill_in "Name", with: "Example User"
fill_in "Email", with: "[email protected]"
fill_in "Password", with: "foobar"
fill_in "Confirmation", with: "foobar"
end
it "should create a user" do
expect { click_button submit }.to change(User, :count).by(1)
end
describe "after saving the user" do
before { click_button submit }
let(:user) { User.find_by_email('[email protected]') }
it { should have_title(user.name) }
it { should have_selector('div.alert.alert-success', text: 'Welcome') }
it { should have_link('Sign out') }
end
end
end
# TESTING EDITING FUNCTIONALITY
describe "edit" do
let(:user) { FactoryGirl.create(:user) }
before do
sign_in user
visit edit_user_path(user)
end
describe "page" do
it { should have_selector('h1', text: "Update your profile") }
it { should have_title("Edit user") }
# it { should have_link('change', href: 'http://gravatar.com/emails') }
# needed if using gravatar
end
describe "with invalid information" do
before { click_button "Save changes" }
it { should have_content('error') }
end
describe "with valid information" do
let(:new_name) { "New Name" }
let(:new_email) { "[email protected]" }
before do
fill_in "Name", with: new_name
fill_in "Email", with: new_email
fill_in "Password", with: user.password
fill_in "Confirm Password", with: user.password
click_button "Save changes"
end
it { should have_title(new_name) }
it { should have_selector('div.alert.alert-success') }
it { should have_link('Sign out', href: signout_path) }
specify { user.reload.name.should == new_name }
specify { user.reload.email.should == new_email }
end
end
end
My spec/support/utilities.rb
include ApplicationHelper
RSpec::Matchers.define :have_error_message do |message|
match do |page|
page.has_selector?('div.alert.alert-error', text: 'Invalid')
end
end
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.
# cookies[:remember_token] = user.remember_token
end
My controllers/users_controller.rb
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:edit, :update]
def new
@user = User.new
end
def show
@user = User.find(params[:id])
end
def create
@user = User.new(params[:user])
if @user.save
sign_in @user
flash[:success] = "Welcome to Authentication App..."
redirect_to @user
else
render 'new'
end
end
def edit
@user = User.find(params[:id])
end
def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
sign_in @user
flash[:success] = "Profile updated"
redirect_to @user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User destroyed"
redirect_to users_path
end
private
def signed_in_user
redirect_to signin_url, notice: "Please sign in." unless signed_in?
end
end
& Lastly my views/users/edit.html.erb
<% provide(:title, "Edit user") %>
<h1>Update your profile</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(@user) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirm Password" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Save changes", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
Any and all help as to the correct fix would be greatly appreciated. thanks,
Upvotes: 1
Views: 1485
Reputation: 4486
If you're following the Rails Tutorial, you need to downgrade your Capybara version to 1.1.2 and move your specs into the spec/requests
directory. Update your Gemfile:
gem 'capybara', '1.1.2'
If you plan on using Capybara 2.0, I suggest reading this:
http://alindeman.github.io/2012/11/11/rspec-rails-and-capybara-2.0-what-you-need-to-know.html
Upvotes: 0
Reputation: 15129
The problem is: you messing request (capybara
) tests with functional tests.
In capybara
you should describe user steps, as such
require 'spec_helper'
describe "Website access" do
context "when I am a registered user" do
it "should let me in" do
page.fill_in 'Email', with: '[email protected]'
page.fill_in 'Password', with: 'mydearluke'
page.click_link 'Let me in'
page.should have_content('Welcome, cheif!')
end
end
context "when I am not a registered user" do
it "should not let me in" do
page.fill_in 'Email', with: '[email protected]'
page.fill_in 'Password', with: 'wormsarmageddon'
page.click_link 'Let me in'
page.should have_content('Incorrect credentials!')
end
end
end
Functional tests operate at a lower level, since you need to use HTTP verbs as put
, get
, post
, delete
to communicate with the application you're testing.
require 'spec_helper'
describe SessionsController do
context "when I am registered user" do
it "should let me in" do
post :create, email: '[email protected]', password: 'mydearluke'
response.should be_success
end
end
context "when I am not a registered user" do
it "should not let me in" do
post :create, email: '[email protected]', password: 'wormsarmageddon'
response.should_not be_success
end
end
end
A few things to notice here:
post
, not put
.describe
block, in contrast to a plain english desctiption like in capybara tests.SessionController
should respond to a different user credentials (it should create a session for a good user, and scream back at a not registered one).What you are trying to do is to use verbs from functional tests in your request (capybara) tests. This is wrong.
A recap:
visit
and click_link
/click_button
in capybara
tests.put
, get
, post
, delete
) in functional tests.devise
gem for user authentication.Upvotes: 1
Reputation: 33181
put, get, post, delete
and those methods used for controller tests are not available in feature tests.
You should be "submitting" the put
through a button action/ajax/etc..., not posting to the controller as you would a controller test, when writing feature tests.
Upvotes: 1