web-dev-qa-db-ja.com

RSpecテスト破棄メソッド(Railsチュートリアル3.2Ch。9、Ex。10)

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

Railsチュートリアルの第9章の演習10では、次のように求められます。[ユーザーの]破棄アクションを変更して、管理ユーザーが自分自身を破棄しないようにします(最初にテストを記述します)。

ここで注意が必要なのはテストです。アプリケーションは現在のユーザーの「削除」リンクをすでに非表示にしているため、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の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

テスト用の宝石:
[。 'growl'、 '1.0.3' gem'guard-spork '、' 0.3.2 'gem'spork'、 '0.9.0' gem'factory_girl_Rails '、' 1.4.0 'end

詳細クリックをシミュレートする方法をトン試してみました削除リンクにあり、どれも機能していないようです。私はデバッガーgemを使用して、destroyメソッドが呼び出されているかどうかを確認しています。リンクをクリックして別のユーザーを削除するテストでは、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リクエストに入れる必要がありました。手動で。これがうまくいったものです。 (以下にリストされている、私が抱えていた他の問題は、destroyアクションが呼び出されない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ステートメントがありましたが、これはどういうわけか物事を捨てていたので、destroyアクションにたどり着きませんでした。

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/

15
will_d

あなたはrspecを混乱させています-統合テストであり、シミュレートされたブラウザーで実行されるRailsリクエストの仕様と、コントローラーを分離してテストするコントローラーの仕様。 delete(action, *args)(およびgetpostなど)-ActionController :: TestCaseからのリクエストをシミュレートするメソッドであるため、テストでは使用できません。

したがって、唯一のオプションは、ブラウザでのクリックをシミュレートすることです。削除リンクを非表示にする方法がわかりません。HTMLはあるが非表示になっている場合は、クリックできるはずです。そこにない場合(ビューの生成時にサーバー側で削除)、capybaraのpage.execute_scriptを使用できます(ただし、この例ではjavascriptを有効にする必要があります:js => true)。リンクを追加し直すこともできます。

page.execute_script("$('body').append("<a href="/users/1" data-method="delete" rel="nofollow">Destroy</a>")")

またはajax呼び出しを行います:

page.execute_script("$.ajax({type:'DELETE',url:'/users/1'})")

テストしませんでしたが、このようなものは動作するはずです。

5
Art Shayderov

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

it "should not be able to delete itself" do
  expect { delete user_path(admin) }.not_to change(User, :count)
end
6
M. David Green

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

describe "should not be able to delete themselves" do
  it { expect { delete user_path(admin) }.not_to change(User, :count) }
end
6
CallumD

これは私が最終的に得たものです(Rspec3.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で指定されているので、私見ではそれを信頼しても大丈夫です。

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

5
Artur Beljajev

これを試して:

expect { delete :destroy, :id => admin }.to_not change(User, :count)
3
DVG