Possible bug in Rails 4 Tutorial, section 9.6, exercise 1

限于喜欢 提交于 2019-12-23 00:29:18

问题


I'm working through the Rails 4 version of Michael Hartl's Rails Tutorial and having trouble with section 9.6 exercise 1 (Listing 9.49).

It looks like the test in the tutorial passes for the wrong reason. Before the PATCH request, user.admin? is false by default; after the PATCH request user.admin? is still false (thus passing the test) because the PATCH request is not getting to the UsersController#update method.

Here's my code:

spec/requests/user_pages_spec.rb (other tests removed to isolate the one in question):

require 'spec_helper'
describe "User pages" do
  subject { page }      
  describe 'edit' do
    let(:user) { FactoryGirl.create(:user) }
    before do
      sign_in user
      visit edit_user_path(user)
    end
    describe "forbidden attributes" do
      let(:params) do
        { user: { name: 'Forbidden Attributes',
                  password: user.password,
                  password_confirmation: user.password,
                  admin: true } }
      end
      before { patch user_path(user), params }
      specify { expect(user.reload).not_to be_admin  }
    end    
  end
end

Relevant parts of app/controllers/users_controller.rb:

class UsersController < ApplicationController
  before_action :signed_in_user, only: [:index, :edit, :update]
  before_action :correct_user,   only: [:edit, :update]

  # PATCH /users/:id
  def update
    # @user is set in before_action
    if @user.update_attributes(user_params)
      # handle a successful update
      flash[:success] = 'Profile updated'
      sign_in @user
      redirect_to @user
    else
      render 'edit'
    end
  end

  private

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

    # Before filters

    def signed_in_user
      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_url) unless current_user?(@user)
    end
end

spec/factories.rb:

FactoryGirl.define do
  factory :user do
    sequence(:name) { |n| "Person #{n}" }
    sequence(:email) { |n| "person_#{n}@example.com" }
    password  "foobar"
    password_confirmation "foobar"

    factory :admin do
      admin true
    end
  end
end

And here's what the test log shows happens:

Started PATCH "/users/2111" for 127.0.0.1 at 2013-08-18 21:30:44 -0400
Processing by UsersController#update as HTML
  Parameters: {"user"=>{"name"=>"Forbidden Attributes", "password"=>"[FILTERED]", \
"password_confirmation"=>"[FILTERED]", "admin"=>"true"}, "id"=>"2111"}
  User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."remember_token" = \
'da39a3ee5e6b4b0d3255bfef95601890afd80709' LIMIT 1
**Redirected to http://www.example.com/signin
Filter chain halted as :signed_in_user rendered or redirected**
Completed 302 Found in 2ms (ActiveRecord: 0.4ms)

I downloaded the reference version of the code from https://github.com/railstutorial/sample_app_rails_4 and ran rspec spec/requests/user_pages_spec.rb. The test log showed the same thing: the PATCH request is being stopped by signed_in_user and never getting to the update method.

When I played around with testing that admin IS set and added some puts statements, it looked like the user that is being signed in is not the same user that is being tested; the user.id stays constant, but the user.name changes. I wonder if it has something to do with the sequence() calls in the factory.

  1. Can anyone verify or refute my findings?
  2. How can I write this test properly?

Found solution

Further investigation seemed to implicate the remember_token. If I move the "forbidden attributes" test out of the "edit" block and add "capybara: true" to the sign_in call, it works. So Listing 9.49 (spec/requests/user_pages_spec.rb) should look like this:

require 'spec_helper'

describe "User pages" do

  subject { page }
  .
  .
  .
  describe "update forbidden attributes" do
    let(:user) { FactoryGirl.create(:user) }
    let(:params) do
      { user: { admin: true, password: user.password,
                password_confirmation: user.password } }
    end
    before do
      sign_in user, no_capybara: true
      patch user_path(user), params 
    end
    specify { expect(user.reload).not_to be_admin }
  end
end

回答1:


I just wanted to confirm that I'm seeing the same behavior when the "forbidden attributes" test is nested within the "edit" test block.

My notes indicate that it was mentioned in Chapter 9 that when ever you're doing a direct POST, PATCH, GET or DELETE request, as opposed to using visit, the no_capybara: true option needs to be given to the sign_in method to ensure the user is signed in.

However, in this case if you use the no_capybara: true option with sign_in the other tests in the "edit" block will fail due to some Capybara issues.

As mentioned by the OP, if the option is omitted, then the "forbidden attributes" tests pass regardless of the presence, or lack thereof, of :admin in the user_params method within the Users controller.




回答2:


Same problem here. With metafour's help, I find the following works. We need to sign in the user with capybara: true for patch to work.

describe "forbidden attributes" do
  let(:params) do
    { user: { admin: true, password: user.password,
              password_confirmation: user.password } }
  end
  before do
    sign_in user, no_capybara: true
    patch user_path(user), params
  end
  specify { expect(user.reload).not_to be_admin }
end


来源:https://stackoverflow.com/questions/18305598/possible-bug-in-rails-4-tutorial-section-9-6-exercise-1

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!