2 votes

Bug possible dans le Tutoriel Rails 4, section 9.6, exercice 1

Je travaille sur la version Rails 4 du Rails Tutorial de Michael Hartl et j'ai des difficultés avec la section 9.6 exercice 1 (Listing 9.49).

Il semble que le test dans le tutoriel passe pour la mauvaise raison. Avant la requête PATCH, user.admin ? est faux par défaut ; après la requête PATCH, user.admin ? est toujours faux (et le test est donc réussi) parce que la requête PATCH ne parvient pas à la méthode UsersController#update.

Voici mon 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

Et voici ce qui se passe dans le journal de test :

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)

J'ai téléchargé la version de référence du code à partir de https://github.com/railstutorial/sample_app_rails_4 et a couru rspec spec/requests/user_pages_spec.rb . Le journal de test montre la même chose : la demande de PATCH est arrêtée par signed_in_user et ne parvient jamais à la méthode de mise à jour.

Lorsque je me suis amusé à tester l'ensemble admin IS et que j'ai ajouté quelques instructions puts, il semble que l'utilisateur qui se connecte n'est pas le même que celui qui est testé ; le user.id reste constant, mais le user.name change. Je me demande si cela n'a pas quelque chose à voir avec les appels sequence() dans la fabrique.

  1. Quelqu'un peut-il vérifier ou réfuter mes conclusions ?
  2. Comment puis-je écrire ce test correctement ?

Solution trouvée

Une enquête plus poussée semblait impliquer le remember_token. Si je déplace le test des "attributs interdits" hors du bloc "edit" et que j'ajoute "capybara : true" à l'appel à l'inscription, cela fonctionne. Ainsi, la liste 9.49 (spec/requests/user_pages_spec.rb) devrait ressembler à ceci :

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

1voto

metafour Points 11

Je voulais juste confirmer que je vois le même comportement lorsque le test "attributs interdits" est imbriqué dans le bloc de test "modifier".

Mes notes indiquent qu'il a été mentionné au chapitre 9 que lorsque vous effectuez une demande directe POST, PATCH, GET ou DELETE, par opposition à l'utilisation de visit, l'option no_capybara : true doit être donnée à la méthode sign_in pour s'assurer que l'utilisateur est connecté.

Toutefois, dans ce cas, si vous utilisez l'option no_capybara : true avec sign_in, les autres tests du bloc "edit" échoueront en raison de certains problèmes liés à Capybara.

Comme le mentionne le PO, si l'option est omise, les tests "attributs interdits" passent indépendamment de la présence, ou non, de :admin dans la méthode user_params du contrôleur Users.

0voto

user1738342 Points 321

Même problème ici. Avec l'aide de metafour, je trouve que ce qui suit fonctionne. Nous devons connecter l'utilisateur avec capybara : true pour que le patch fonctionne.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X