注:この質問と回答を読みましたが、何らかの理由でコードが機能しません。(私が得ているエラーについては以下を参照してください)
Rails チュートリアルの第 9 章の演習 10 では、[ユーザーの] destroy アクションを変更して、管理者ユーザーが自分自身を破棄できないようにします。(最初にテストを書きます。)
アプリケーションはすでに現在のユーザーの「削除」リンクを隠しているため、http 要求を直接実行する必要があるため、ここで注意が必要なのはテストです。
コードが機能するようになり、現在のユーザーの削除リンクを非表示にするコードを削除してテストしました。案の定、現在ログインしているユーザーの削除リンクをクリックすると、リダイレクトされて通知メッセージが表示されます。
users_controller.rb から
def destroy
@user = User.find(params[:id])
if current_user?(@user)
redirect_to users_path, notice: "You can't destroy yourself."
else
@user.destroy
flash[:success] = "User destroyed."
redirect_to users_path
end
end
私が抱えている問題は、削除要求を送信して destroy メソッドを呼び出す、このためのテストを作成することです。私はここにコピーしているdestroy if no delete link のRspecテストからの解決策を試しました:
user_pages_spec.rb から
describe "destroy" do
let(:admin) { FactoryGirl.create(:admin) }
it "should not allow the admin to delete herself" do
sign_in admin
#expect { delete user_path(admin), method: :delete }.should change(User, :count)
expect { delete :destroy, :id => admin.id }.should_not change(User, :count)
end
end
しかし、これを実行すると、RSpec からこのエラーが発生します
Failures:
1) User Pages destroy should not allow the admin to delete herself
Failure/Error: expect { delete :destroy, :id => admin.id }.should_not change(User, :count)
ArgumentError:
bad argument (expected URI object or URI string)
# ./spec/requests/user_pages_spec.rb:180:in `block (4 levels) in <top (required)>'
# ./spec/requests/user_pages_spec.rb:180:in `block (3 levels) in <top (required)>'
だから、私の質問は次のとおりです。1)上記のコードが失敗するのはなぜですか?2)コントローラーで破棄アクションを呼び出すために「削除」をシミュレートするにはどうすればよいですか?
環境: Mac OSX ruby 1.9.3p194 Rails 3.2.3
テスト用の Gem:
group :test do gem 'rspec-rails', '2.9.0' gem 'capybara', '1.1.2' gem 'rb-fsevent', '0.4.3.1', :require => false gem ' growl', '1.0.3' gem 'guard-spork', '0.3.2' gem 'spork', '0.9.0' gem 'factory_girl_rails', '1.4.0' end
詳細情報削除リンクのクリックをシミュレートする方法をたくさん試しましたが、うまくいかないようです 。destroy メソッドが呼び出されているかどうかを確認するために、debugger gem を使用しています。リンクをクリックして別のユーザーを削除するテストでは、destroy メソッドが呼び出され、正常に動作します。
it "should be able to delete another user" do
expect { click_link('delete') }.to change(User, :count).by(-1)
end
しかし、destroy メソッドを呼び出すために削除リクエストを直接生成しようとしたものは何もありません。
ご協力いただきありがとうございます!
意思
** アップデート **
DVGの提案を試しました:
describe "destroy" do
let(:admin) { FactoryGirl.create(:admin) }
it "should not allow the admin to delete herself" do
sign_in admin
#expect { delete user_path(admin), method: :delete }.should change(User, :count)
expect { delete :destroy, :id => admin }.to_not change(User, :count)
end
end
そして、このエラーが発生しました:
6) User Pages destroy should not allow the admin to delete herself
Failure/Error: expect { delete :destroy, :id => admin }.to_not change(User, :count)
ArgumentError:
bad argument (expected URI object or URI string)
# ./spec/requests/user_pages_spec.rb:190:in `block (4 levels) in <top (required)>'
# ./spec/requests/user_pages_spec.rb:190:in `block (3 levels) in <top (required)>'
解決
FOREVERの後でそれを理解しました。
Rack::Test を使用して DELETE リクエストを発行する必要がありましたが、Capybara と Rack::Test は同じ MockSession を共有していないため、:remember_token および :!sample_app_session Cookie を取得して DELETE リクエストに入れる必要がありました。手動で。これが機能したものです。(私が抱えていた他の問題は、以下にリストされていますが、破棄アクションが呼び出されない force_ssl ステートメントがあったことです。
describe "destroy" do
let!(:admin) { FactoryGirl.create(:admin) }
before do
sign_in admin
end
it "should delete a normal user" do
user = FactoryGirl.create(:user)
expect { delete user_path(user), {},
'HTTP_COOKIE' => "remember_token=#{admin.remember_token},
#{Capybara.current_session.driver.response.headers["Set-Cookie"]}" }.
to change(User, :count).by(-1)
end
it "should not allow the admin to delete herself" do
expect { delete user_path(admin), {},
'HTTP_COOKIE' => "remember_token=#{admin.remember_token},
#{Capybara.current_session.driver.response.headers["Set-Cookie"]}" }.
to_not change(User, :count)
end
end
users_controller.rb の before_filters の後に force_ssl ステートメントがありましたが、これがどういうわけか物事を台無しにしていたため、破棄アクションに到達しませんでした。
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:edit, :update, :index]
before_filter :existing_user, only: [:new, :create]
before_filter :correct_user, only: [:edit, :update]
before_filter :admin_user, only: :destroy
#force_ssl
def index
@users = User.paginate(page: params[:page])
end
def show
@user = User.find(params[:id])
@microposts = @user.microposts.paginate(page: params[:page])
end
def destroy
@user = User.find(params[:id])
if current_user?(@user)
redirect_to users_path, notice: "You can't destroy yourself."
else
@user.destroy
flash[:success] = "User destroyed."
redirect_to users_path
end
end
これらは解決策を得るのに役立ちました
https://gist.github.com/484787
http://collectiveidea.com/blog/archives/2012/01/05/capybara-cucumber-and-how-the-cookie-crumbles/