web-dev-qa-db-ja.com

RSpecでログインをシミュレートするにはどうすればよいですか?

私はここ数年、Railsで遊んでいて、実稼働中のいくつかのパス可能なアプリを作成しました。しかし、テストを行うことは常に避け、修正することにしました。すでに稼働しているが、常に改訂されている作業用に作成したアプリのテストを作成しようとしています。変更があると問題が発生する可能性があるので、テストを実行する必要があります。 RSpecの本を読み、いくつかのスクリーンキャストを見ましたが、始めるのに苦労しています(実際にやった後にしか理解できないようなものだと思います)。

ReportsControllerの簡単なテストとなるものを書き込もうとしています。私のアプリの問題は、ほとんどすべてが認証レイヤーの背後にあることです。ログインしていない場合は何も機能しないので、単純なgetリクエストを送信する前にログインをシミュレートする必要があります(ただし、ログインなしでは何も機能しないことを確認するためのテストを作成する必要があると思いますが、後で)。

RSpec、Capybara、FactoryGirl、およびGuardを使用してテスト環境をセットアップしました(使用するツールが不明なため、Railscastsの提案を使用しました)。これまでにテストを記述してきた方法は、FactoryGirlでユーザーを作成することです。

FactoryGirl.define do
  sequence(:email) {|n| "user#{n}@example.com"}
  sequence(:login) {|n| "user#{n}"}
  factory :user do
    email {FactoryGirl.generate :email}
    login {FactoryGirl.generate :login}
    password "abc"
    admin false
    first_name "Bob"
    last_name "Bobson"
  end
end

そして、そのように私のテストを書きます。

require 'spec_helper'

describe ReportsController do
  describe "GET 'index'" do
    it "should be successful" do
      user = Factory(:user)
      visit login_path
      fill_in "login", :with => user.login
      fill_in "password", :with => user.password
      click_button "Log in"
      get 'index'
      response.should be_success
    end
  end
end

これはそのように失敗します。

  1) ReportsController GET 'index' should be successful
     Failure/Error: response.should be_success
       expected success? to return true, got false
     # ./spec/controllers/reports_controller_spec.rb:13:in `block (3 levels) in <top (required)>'

興味深いことに、テストをresponse.should be_redirectに変更すると、テストは成功し、その時点まではすべてが正常に機能しているが、ログインが認識されていないことを示唆します。

私の質問は、このログインを機能させるために何をしなければならないかです。 FactoryGirl資格情報と一致するユーザーをデータベースに作成する必要がありますか?もしそうなら、ここでFactoryGirlのポイントは何ですか(そして私もそれを使用する必要があります)?テスト環境でこの偽のユーザーを作成するにはどうすればよいですか?私の認証システムは非常にシンプルな自作システムです( Railscasts episode 25 に基づいています)。このログイン動作は、ほとんどすべてのテストで複製する必要がありますが、コードで一度実行してどこにでも適用するにはどうすればよいですか?

これは大きな質問だと思いますので、ご覧いただきありがとうございます。

49
brad

答えは、認証の実装によって異なります。通常、ユーザーがログインすると、セッション変数を設定して、そのユーザーを記憶します(_session[:user_id]_など)。コントローラは_before_filter_でログインをチェックし、そのようなセッション変数が存在しない場合はリダイレクトします。あなたはすでにこのようなことをしていると思います。

これをテストで機能させるには、ユーザー情報をセッションに手動で挿入する必要があります。職場で使用するものの一部を次に示します。

_# spec/support/spec_test_helper.rb
module SpecTestHelper   
  def login_admin
    login(:admin)
  end

  def login(user)
    user = User.where(:login => user.to_s).first if user.is_a?(Symbol)
    request.session[:user] = user.id
  end

  def current_user
    User.find(request.session[:user])
  end
end

# spec/spec_helper.rb
RSpec.configure do |config|
  config.include SpecTestHelper, :type => :controller
end
_

コントローラーの例では、login(some_user)を呼び出して、そのユーザーとしてのログインをシミュレートできます。


また、このコントローラーテストで統合テストを行っているように見えることにも言及する必要があります。原則として、コントローラーテストは、次のような個々のコントローラーアクションへのリクエストのみをシミュレートする必要があります。

_it 'should be successful' do
  get :index
  response.should be_success
end
_

これは、単一のコントローラーアクションを具体的にテストします。これは、コントローラーテストのセットで必要なものです。次に、Capybara/Cucumberを使用して、フォーム、ビュー、コントローラーのエンドツーエンドの統合テストを実行できます。

45
Brandan

spec/support/controller_helpers.rbにヘルパーファイルを追加し、以下のコンテンツをコピーします

module ControllerHelpers
    def sign_in(user)
      if user.nil?
        allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, {:scope => :user})
        allow(controller).to receive(:current_user).and_return(nil)
      else
        allow(request.env['warden']).to receive(:authenticate!).and_return(user)
        allow(controller).to receive(:current_user).and_return(user)
      end
    end
  end

spec/Rails_helper.rbまたはspec/spec_helper.rbファイル

require 'support/controller_helpers'

RSpec.configure do |config|

    config.include Devise::TestHelpers, :type => :controller
    config.include ControllerHelpers, :type => :controller

  end

これで、コントローラの仕様ファイルに。

describe  "GET #index" do

    before :each do        
        @user=create(:user)
        sign_in @user
    end
      ...
end

Devise公式リンク

8

@Brandanの回答を機能させることはできませんでしたが、それに基づいて この投稿 に基づいて、この解決策に到達しました。

# spec/support/Rails_helper.rb
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } # Add this at top of file

...

include ControllerMacros # Add at bottom of file

そして

# spec/support/controller_macros.rb
module ControllerMacros

  def login_as_admin
    admin = FactoryGirl.create(:user_admin)
    login_as(admin)
  end

  def login_as(user)
    request.session[:user_id] = user.id
  end

end

その後、テストで次を使用できます。

it "works" do
  login_as(FactoryGirl.create(:user))
  expect(request.session[:user_id]).not_to be_nil
end
2
Bruno Peres

機能テストでユーザーとログインする最も簡単な方法は、Wardenの helper #login_as を使用することです

login_as some_user
0
MegaTux