0

私は Michael Hartl の素晴らしいRuby on Rails チュートリアルを行っていますが、多くの人がChapter 9, Exercise 9 で問題を抱えているのを見てきました。私もかなりの問題を抱えていましたが、正確な問題(または解決策)を他に見当たりませんでした。ここでは参照用であり、他の人に役立つことを願っています。私の環境は Ruby 2.1.1、Rails 4.1.4、RSpec 3.0.0、Capybara 2.4.1 です。本に書かれている環境とは異なりますが、最新のツールやバージョンを使うのも楽しみの一つでした。

問題は、テストを設計し、UsersController#destroyアクションを変更して、管理者ユーザーが自分自身を削除できないようにすることです。ユーザーページの「削除」リンクはユーザーごとに非表示になっているので、 が付いているユーザーでも、 にリクエストをadmin: true発行する方法です。DELETEusers_path(user)

describe 'Admin cannot delete himself/herself' do
  let(:admin_user) { FactoryGirl.create(:admin) }
  before { log_in admin_user }
  specify { expect { delete user_path(admin_user) }.not_to change(User, :count) }  
end

ただし、このテストを実行すると合格します。

$ rspec -e "cannot delete himself"
Run options: include {:full_description=>/cannot\ delete\ himself/}
.

Finished in 1.59 seconds (files took 0.98179 seconds to load)
1 example, 0 failures

何か怪しい。からの出力は次のlog/test.logとおりです。

ActiveRecord::SchemaMigration Load (0.1ms)  SELECT "schema_migrations".* FROM "schema_migrations"
   (0.1ms)  begin transaction
   (0.0ms)  SAVEPOINT active_record_1
  User Exists (0.1ms)  SELECT  1 AS one FROM "users"  WHERE LOWER("users"."email") = LOWER('person_1@example.com') LIMIT 1
Binary data inserted for `string` type on column `password_digest`
  SQL (0.2ms)  INSERT INTO "users" ("admin", "created_at", "email", "name", "password_digest", "remember_token", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?)  [["admin", "t"], ["created_at", "2014-08-13 13:56:11.310082"], ["email", "person_1@example.com"], ["name", "Person 1"], ["password_digest", "$2a$04$.ScHVB84mm4/4G12Vcpudu/k741nyrXM4vLtZa7XgtecNB6uOHNPy"], ["remember_token", "c527083d0ba4581ed92f258eede7d1377dcf7d5b"], ["updated_at", "2014-08-13 13:56:11.310082"]]
   (0.0ms)  RELEASE SAVEPOINT active_record_1
Started GET "/login" for 127.0.0.1 at 2014-08-13 15:56:11 +0200
Processing by SessionsController#new as HTML
  Rendered sessions/new.html.erb within layouts/application (1.8ms)
  Rendered layouts/_internet_explorer_shim.html.erb (0.3ms)
  User Load (0.1ms)  SELECT  "users".* FROM "users"  WHERE "users"."remember_token" = 'da39a3ee5e6b4b0d3255bfef95601890afd80709' LIMIT 1
  Rendered layouts/_header.html.erb (3.0ms)
  Rendered layouts/_footer.html.erb (0.4ms)
Completed 200 OK in 1456ms (Views: 1450.2ms | ActiveRecord: 0.1ms)
Started POST "/sessions" for 127.0.0.1 at 2014-08-13 15:56:12 +0200
Processing by SessionsController#create as HTML
  Parameters: {"utf8"=>"✓", "email"=>"person_1@example.com", "password"=>"[FILTERED]", "commit"=>"Log in"}
  User Load (0.1ms)  SELECT  "users".* FROM "users"  WHERE "users"."email" = 'person_1@example.com' LIMIT 1
   (0.0ms)  SAVEPOINT active_record_1
  SQL (0.2ms)  UPDATE "users" SET "remember_token" = ?, "updated_at" = ? WHERE "users"."id" = 362  [["remember_token", "3df781e414c64f2f72cf1e08594f92595922329a"], ["updated_at", "2014-08-13 13:56:12.870234"]]
   (0.0ms)  RELEASE SAVEPOINT active_record_1
Redirected to http://www.example.com/users/362
Completed 302 Found in 4ms (ActiveRecord: 0.4ms)
Started GET "/users/362" for 127.0.0.1 at 2014-08-13 15:56:12 +0200
Processing by UsersController#show as HTML
  Parameters: {"id"=>"362"}
  User Load (0.1ms)  SELECT  "users".* FROM "users"  WHERE "users"."id" = ? LIMIT 1  [["id", 362]]
  Rendered users/show.html.erb within layouts/application (0.4ms)
  Rendered layouts/_internet_explorer_shim.html.erb (0.0ms)
  User Load (0.1ms)  SELECT  "users".* FROM "users"  WHERE "users"."remember_token" = '3df781e414c64f2f72cf1e08594f92595922329a' LIMIT 1
  Rendered layouts/_header.html.erb (1.0ms)
  Rendered layouts/_footer.html.erb (0.1ms)
Completed 200 OK in 5ms (Views: 3.5ms | ActiveRecord: 0.2ms)
   (0.1ms)  SELECT COUNT(*) FROM "users"
Started DELETE "/users/362" for 127.0.0.1 at 2014-08-13 15:56:12 +0200
Processing by UsersController#destroy as HTML
  Parameters: {"id"=>"362"}
  User Load (0.1ms)  SELECT  "users".* FROM "users"  WHERE "users"."remember_token" = 'da39a3ee5e6b4b0d3255bfef95601890afd80709' LIMIT 1
  CACHE (0.0ms)  SELECT  "users".* FROM "users"  WHERE "users"."remember_token" = 'da39a3ee5e6b4b0d3255bfef95601890afd80709' LIMIT 1
Redirected to http://www.example.com/login
Filter chain halted as :logged_in_user rendered or redirected
Completed 302 Found in 1ms (ActiveRecord: 0.1ms)
   (0.1ms)  SELECT COUNT(*) FROM "users"
   (0.1ms)  rollback transaction

ユーザーを削除しようとすると、フィルタが

before_action :logged_in_user, only: [:index, :edit, :update, :destroy]

ログの次の行で示唆されているように、キックインします。

Redirected to http://www.example.com/login
Filter chain halted as :logged_in_user rendered or redirected

何らかの理由でユーザーがログインせず、保護されたページに到達すると、ログイン ページにリダイレクトされます。ブロック内の行にlogger.warn "<message>"inを追加するUsersController#destroyか、行のputs User.count後に追加することで、ユーザーが削除されていないことを実際に確認できます。expectspecify

:admin_userログインしていないのはなぜですか?

4

2 に答える 2

0

根本的な問題は、ログイン (訪問、クリック) などにカピバラ メソッドを使用することになりましたが、削除要求には Rails 統合テスト メソッドを使用することになりました。

これら 2 つは完全に分離されているため、ログイン プロセスによって設定された Cookie は、削除要求では使用されません。リンク先のページでは、それらはそうであることに注意してください

before { sign_in non_admin, no_capybara: true }

これにより、sign_inヘルパーは、ページに移動したり、フォームに入力したりする代わりに、Cookie を直接設定します。

于 2014-08-13T14:38:55.227 に答える
0

問題は、要するに、Capybara のモック セッションが RSpec のものと異なることです。ログインはCapybaraで行いますが、DELETEリクエストはRSpecから直接送信され、remember_tokenRSpecが管理者ユーザーを削除しようとすると、データベースに保存されているものが一致しません。

純粋な Capybara シーケンスのアクションを実行するには、Rails コンソールでこの Gistを使用できます(長い出力は省略します)。

$ rails c -e test
Loading test environment (Rails 4.1.4)
2.1.1 :001 > User.create(name: 'Foo Bar', email: 'foo@bar.net', password: 'foobar', password_confirmation: 'foobar', admin: true)
...
2.1.1 :002 > Capybara.app = app.instance_variable_get("@app"); nil
 => nil 
2.1.1 :003 > cap = Object.new.instance_eval { extend Capybara::DSL; self }
 => #<Object:0x00000004a5b758> 
2.1.1 :004 > cap.visit app.login_path
...
2.1.1 :005 > cap.fill_in 'Email', with: 'foo@bar.net'
 => "foo@bar.net" 
2.1.1 :006 > cap.fill_in 'Password', with: 'foobar'
 => "foobar" 
2.1.1 :007 > cap.click_button 'Log in'
...
2.1.1 :009 > admin_user = User.find_by(email: 'foo@bar.net')
...
2.1.1 :010 > cap.page.driver.delete app.user_path(admin_user)
...
2.1.1 :011 > User.count
   (0.1ms)  SELECT COUNT(*) FROM "users"
 => 0 
2.1.1 :013 > 

また、質問の元のログを見ると、ログイン後のログがリクエストremember_tokenで設定されたログと異なることがわかります。DELETE

要約すると、テストを記述する正しい方法は次のno_capybara: trueとおりです。

describe 'Admin cannot delete himself/herself' do
  let(:admin_user) { FactoryGirl.create(:admin) }
  before { log_in admin_user, no_capybara: true }
  specify { expect { delete user_path(admin_user) }.not_to change(User, :count) }  
end

これにより、次の結果が得られます。

$ rspec -e "cannot delete himself"
Run options: include {:full_description=>/cannot\ delete\ himself/}
F

Failures:

  1) Admin cannot delete himself with a DELETE request should not change #count
     Failure/Error: specify { expect { delete user_path(admin_user) }.not_to change(User, :count) }
       expected #count not to have changed, but did change from 1 to 0
     # ./spec/requests/admin_autodelete_spec.rb:7:in `block (2 levels) in <top (required)>'

Finished in 0.04263 seconds (files took 0.99411 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/requests/admin_autodelete_spec.rb:7 # Admin cannot delete himself with a DELETE request should not change #count

これで、演習の解決に進むことができます。これが役立つことを願っています!

于 2014-08-13T14:42:31.163 に答える