问题
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.
- Can anyone verify or refute my findings?
- 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