Question

I'm having trouble debugging my Rspec code for the Rails Tutorial. When I run my rspec code, I get this error:

1) Authentication authorization as non-admin user submitting a DELETE request to the Users#destroy action
 Failure/Error: specify { expect(response).to redirect_to(root_url) }
   Expected response to be a redirect to <http://www.example.com/> but was a redirect to <http://www.example.com/signin>.
   Expected "http://www.example.com/" to be === "http://www.example.com/signin".
 # ./spec/requests/authentication_pages_spec.rb:106:in `block (5 levels) in <top (required)>'

2) Authentication authorization as wrong user submitting a GET request to the Users#edit action
 Failure/Error: specify { expect(response).to redirect_to(root_url) }
   Expected response to be a redirect to <http://www.example.com/> but was a redirect to <http://www.example.com/signin>.
   Expected "http://www.example.com/" to be === "http://www.example.com/signin".
 # ./spec/requests/authentication_pages_spec.rb:90:in `block (5 levels) in <top (required)>'

3) Authentication authorization as wrong user submitting a PATCH request to the Users#update action
 Failure/Error: specify { expect(response).to redirect_to(root_url) }
   Expected response to be a redirect to <http://www.example.com/> but was a redirect to <http://www.example.com/signin>.
   Expected "http://www.example.com/" to be === "http://www.example.com/signin".
 # ./spec/requests/authentication_pages_spec.rb:95:in `block (5 levels) in <top (required)>'

The code for my /spec/requests/authentication_pages_spec.rb is:

require 'spec_helper'

describe "Authentication" do

  subject { page }

  describe "signin page" do
    before { visit signin_path }

    it { should have_content('Sign in') }
    it { should have_title('Sign in') }
  end

  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('Invalid') }

      describe "after visiting another page" do
        before { click_link "Home" }
        it { should_not have_error_message('Invalid') }
      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('Users',       href: users_path) }
      it { should have_link('Profile',     href: user_path(user)) }
      it { should have_link('Settings',    href: edit_user_path(user)) }
      it { should have_link('Sign out',    href: signout_path) }
      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
  describe "authorization" do
    describe "for non-signed-in users" do
      let(:user) { FactoryGirl.create(:user) }

      describe "when attempting to visit a protected page" do
        before do
          visit edit_user_path(user)
          valid_signin(user)
        end

        describe "after signing in" do
          it "should render the desired protected page" do
            expect(page).to have_title('Edit user')
          end
        end
      end

      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 { patch user_path(user) }
          specify { expect(response).to redirect_to(signin_path) }
        end
        describe "visiting the user index" do
          before { visit users_path }
          it { should have_title('Sign in') }
        end
      end
    end
    describe "as wrong user" do
      let(:user) { FactoryGirl.create(:user) }
      let(:wrong_user) { FactoryGirl.create(:user, email: "wrong@example.com") }
      before { sign_in user, no_capybara: true }

      describe "submitting a GET request to the Users#edit action" do
        before { get edit_user_path(wrong_user) }
        specify { expect(response.body).not_to match(full_title('Edit user')) }
        specify { expect(response).to redirect_to(root_url) }
      end

      describe "submitting a PATCH request to the Users#update action" do
        before { patch user_path(wrong_user) }
        specify { expect(response).to redirect_to(root_url) }
      end
    end
    describe "as non-admin user" do
      let(:user) { FactoryGirl.create(:user) }
      let(:non_admin) { FactoryGirl.create(:user) }

      before { sign_in non_admin, no_capybara: true }

      describe "submitting a DELETE request to the Users#destroy action" do
        before { delete user_path(user) }
        specify { expect(response).to redirect_to(root_url) }
      end
    end
  end
end

And the code for my utilities.rb is:

include ApplicationHelper

def valid_signin(user)
  fill_in "Email",    with: user.email
  fill_in "Password", with: user.password
  click_button "Sign in"
end

def sign_in(user, options={})
  if options[:no_capybara]
    # Sign in when not using Capybara.
    remember_token = User.new_remember_token
    cookies[:remember_token] = remember_token
    user.update_attribute(:remember_token, User.encrypt(remember_token))
  else
    visit signin_path
    fill_in "Email",    with: user.email
    fill_in "Password", with: user.password
    click_button "Sign in"
  end
end

def valid_user()
  fill_in "Name",         with: "Example User"
  fill_in "Email",        with: "user@example.com"
  fill_in "Password",     with: "foobar"
  fill_in "Confirmation", with: "foobar"
end

RSpec::Matchers.define :have_error_message do |message|
  match do |page|
    expect(page).to have_selector('div.alert.alert-error', text: message)
  end
end

When I run the rails server, everything works perfectly.

The interesting thing is that my logs are different between when I run it on my server and when I run the actual tests. On the server, logged in as user id 2, I get this:

Started GET "/users/1/edit" for 127.0.0.1 at 2013-12-31 21:28:31 -0800
Processing by UsersController#edit as HTML
  Parameters: {"id"=>"1"}
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
  User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", "1"]]
Redirected to http://localhost:3000/
Filter chain halted as :correct_user rendered or redirected
Completed 302 Found in 4ms (ActiveRecord: 0.2ms)

In my test.log file, I see this:

Binary data inserted for `string` type on column `password_digest`
  [1m[35mSQL (0.4ms)[0m  INSERT INTO "users" ("created_at", "email", "name", "password_digest", "remember_token", "updated_at") VALUES (?, ?, ?, ?, ?, ?)  [["created_at", Sat, 28 Dec 2013 18:50:19 UTC +00:00], ["email", "michael@example.com"], ["name", "Michael Hartl"], ["password_digest", "$2a$04$qTOUv775ioIiAdufscqASuTZBoJEafqvDzc/.FGqmFR30NzQPalLa"], ["remember_token", "698696f7cfbefa641e2b87450b57499b056932ff"], ["updated_at", Sat, 28 Dec 2013 18:50:19 UTC +00:00]]
  [1m[36m (0.0ms)[0m  [1mRELEASE SAVEPOINT active_record_1[0m
Started GET "/users/1/edit" for 127.0.0.1 at 2013-12-28 10:50:19 -0800
Processing by UsersController#edit as HTML
  Parameters: {"id"=>"1"}
Redirected to http://www.example.com/signin
Filter chain halted as :signed_in_user rendered or redirected
Completed 302 Found in 1ms (ActiveRecord: 0.0ms)

Edit1: And here is my signed_in_user code:

  private

    def user_params
      params.require(:user).permit(:admin, :name, :email, :password, :password_confirmation)
    end
    # Before filters

    def signed_in_user
      debugger
      unless signed_in?
        store_location
        redirect_to signin_url, notice: "Please sign in."
      end
    end

    def correct_user
      @user = User.find(params[:id])
      redirect_to root_path unless current_user?(@user)
    end

    def admin_user
      redirect_to root_path unless current_user.admin?
    end

    def strict_signed_in_user
      redirect_to root_url, notice: "You are already signed in." unless !signed_in?
    end

Edit2: Using debugger I've managed to find a few errors like this:

F/Users/rasheedbustamam/Rails projects/sample_app/spec/support/utilities.rb:11
if options[:no_capybara]

[6, 15] in /Users/rasheedbustamam/Rails projects/sample_app/spec/support/utilities.rb
   6    click_button "Sign in"
   7  end
   8
   9  def sign_in(user, options={})
   10    debugger
=> 11    if options[:no_capybara]
   12      # Sign in when not using Capybara.
   13      remember_token = User.new_remember_token
   14      cookies[:remember_token] = remember_token
   15      user.update_attribute(:remember_token, User.encrypt(remember_token))
(rdb:1) v l
self = #<RSpec::Core::ExampleGroup::Nested_1::Nested_3::Nested_2::Nested_2:0x00000109aeb1b8>
options = {:no_capybara=>true}
remember_token = nil
user = #<User:0x00000109af02f8>

Of note is the fact that remember_token is nil which seems like it's a problem.

Thanks so much in advance!

Was it helpful?

Solution

Based on the output from your test, your signed_in_user filter is causing a redirect, indicating that the login has failed. I'd suggest tracing the login execution through your controller code and/or sharing the relevant code here for review.

Update: As a follow-up to the comment thread, the Hartl tutorial has been used and verified by thousands of users, while there are occasional defects as Michael upgrades to accommodate new versions, in general, it is quite accurate and you'll be ok if you follow it carefully. One thing you cannot do is to mix and match software from various versions of the tutorial.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top