BB500
BB500

Reputation: 559

Rspec test failing miserably

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

Answers (3)

Jordan Allan
Jordan Allan

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

oldhomemovie
oldhomemovie

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:

  1. I'm using post, not put.
  2. I'm specifying controller name in describe block, in contrast to a plain english desctiption like in capybara tests.
  3. Functional tests specifies how exactly a 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:

  1. use visit and click_link/click_button in capybara tests.
  2. use HTTP verbs (put, get, post, delete) in functional tests.
  3. use devise gem for user authentication.

Upvotes: 1

nowk
nowk

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

Related Questions