15

注:この質問と回答を読みましたが、何らかの理由でコードが機能しません。(私が得ているエラーについては以下を参照してください)

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/

4

5 に答える 5

6

私は以下を使用してこれと同じ問題を解決しました:

describe "should not be able to delete themselves" do
  it { expect { delete user_path(admin) }.not_to change(User, :count) }
end
于 2012-07-27T22:37:56.923 に答える
6

CallumD のソリューションは私にとってはうまくいき、Michael Hartl のチュートリアルの残りの部分で推奨されている手法と最も一致しているように見えました。しかし、同じチュートリアルの他の仕様との一貫性を高めるために、構文を少し厳密にしたかったのです。

it "should not be able to delete itself" do
  expect { delete user_path(admin) }.not_to change(User, :count)
end
于 2013-02-22T18:34:32.660 に答える
5

これは私が最終的に得たものです(Rspec 3.2):

describe 'DELETE destroy' do
  before :each do
    delete :destroy, { id: current_partner_role }
  end

  it 'destroys role' do
    expect(assigns(:role).destroyed?).to be true
  end

"破壊されました?" メソッド自体はRailsによって仕様化されているため、IMHOに依存しても問題ありません。

https://github.com/rails/rails/blob/5142d5411481c893f817c1431b0869be3745060f/activerecord/lib/active_record/persistence.rb#L91

于 2015-03-12T13:10:01.843 に答える
3

これを試して:

expect { delete :destroy, :id => admin }.to_not change(User, :count)
于 2012-06-12T03:25:59.027 に答える