リクエスト仕様でのスタブ認証


84

リクエストスペックを作成するとき、セッションやスタブコントローラーメソッドをどのように設定しますか?統合テストで認証をスタブアウトしようとしています-rspec / requests

これがテストの例です

require File.dirname(__FILE__) + '/../spec_helper'
require File.dirname(__FILE__) + '/authentication_helpers'


describe "Messages" do
  include AuthenticationHelpers

  describe "GET admin/messages" do
    before(:each) do
      @current_user = Factory :super_admin
      login(@current_user)
    end

    it "displays received messages" do
      sender = Factory :jonas
      direct_message = Message.new(:sender_id => sender.id, :subject => "Message system.", :content => "content", :receiver_ids => [@current_user.id])
      direct_message.save
      get admin_messages_path
      response.body.should include(direct_message.subject) 
    end
  end
end

ヘルパー:

module AuthenticationHelpers
  def login(user)
    session[:user_id] = user.id # session is nil
    #controller.stub!(:current_user).and_return(user) # controller is nil
  end
end

そして、認証を処理するApplicationController:

class ApplicationController < ActionController::Base
  protect_from_forgery

  helper_method :current_user
  helper_method :logged_in?

  protected

  def current_user  
    @current_user ||= User.find(session[:user_id]) if session[:user_id]  
  end

  def logged_in?
    !current_user.nil?
  end
end

これらのリソースにアクセスできないのはなぜですか?

1) Messages GET admin/messages displays received messages
     Failure/Error: login(@current_user)
     NoMethodError:
       undefined method `session' for nil:NilClass
     # ./spec/requests/authentication_helpers.rb:3:in `login'
     # ./spec/requests/message_spec.rb:15:in `block (3 levels) in <top (required)>'

回答:


101

リクエストスペックは薄いラッパーでありActionDispatch::IntegrationTest、コントローラースペック(ラップするActionController::TestCase)のようには機能しません。利用可能なセッションメソッドがありますが、サポートされているとは思いません(つまり、他のユーティリティに含まれるモジュールにもそのメソッドが含まれているため、おそらくサポートされています)。

ユーザーの認証に使用するアクションに投稿してログインすることをお勧めします。すべてのユーザーファクトリのパスワード「password」(たとえば)を作成すると、次のようなことができます。

def login(user)
  post login_path、:login => user.login、:password => 'password'
終わり

1
デビッドに感謝します。それはうまく機能しますが、それらすべての要求を行うのは少しやり過ぎのようですか?
Jonas Nielsen

19
それがやり過ぎだと思ったら、私はそれをお勧めしなかっただろう:)
David Chelimsky 2011

6
これは、確実に行うための最も簡単な方法でもあります。ActionDispatch::IntegrationTest実際のブラウザを使用せずに、ブラウザを介して対話する1人以上のユーザーをシミュレートするように設計されています。1つの例の中に複数のユーザー(つまりセッション)と複数のコントローラーが存在する可能性があり、セッション/コントローラーオブジェクトは最後のリクエストで使用されたものです。リクエストの前にそれらにアクセスすることはできません。
David Chelimsky 2011

17
私が使用する必要がpage.driver.postカピバラと
イアン・ヤン

CapybaraのJonasNicklasとテストAPIpage.driver.postによると、@ IanYangはアンチパターンである可能性があります
Epigene 2017

61

Deviseユーザーへの注意...

あなたが使用している場合ところで、@デビッドChelimskyの答えは少し調整が必要になる場合があり工夫を。私が統合/リクエストテストで行っていること(このStackOverflowの投稿に感謝します):

# file: spec/requests_helper.rb
def login(user)
  post_via_redirect user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
end

2
次に、rspecモデル仕様で「loginuser1」を使用すると、#<RSpec :: Core:
jpw

1
これはdevise_for :usersconfig/routes.rbファイルにあることを前提としています。別のものを指定した場合は、それに応じてコードを微調整する必要があります。
fearless_fool

これは私にとってはうまくいきましたが、少し変更する必要がありました。アプリがメールではなくユーザー名をログインとして使用'user[email]' => user.emailする'user[username]' => user.usernameため、に変更しました。
webdevguy 2016年

3

FWIW、Test :: UnitテストをRSpecに移植する際に、リクエストスペックで複数の(デバイス)セッションでログインできるようにしたかったのです。少し掘り下げましたが、これでうまくいきました。Rails3.2.13とRSpec2.13.0の使用。

# file: spec/support/devise.rb
module RequestHelpers
  def login(user)
    ActionController::IntegrationTest.new(self).open_session do |sess|
      u = users(user)

      sess.post '/users/sign_in', {
        user: {
          email: u.email,
          password: 'password'
        }
      }

      sess.flash[:alert].should be_nil
      sess.flash[:notice].should == 'Signed in successfully.'
      sess.response.code.should == '302'
    end
  end
end

include RequestHelpers

そして...

# spec/request/user_flows.rb
require 'spec_helper'

describe 'User flows' do
  fixtures :users

  it 'lets a user do stuff to another user' do
    karl = login :karl
    karl.get '/users'
    karl.response.code.should eq '200'

    karl.xhr :put, "/users/#{users(:bob).id}", id: users(:bob).id,
      "#{users(:bob).id}-is-funny" => 'true'

    karl.response.code.should eq '200'
    User.find(users(:bob).id).should be_funny

    bob = login :bob
    expect { bob.get '/users' }.to_not raise_exception

    bob.response.code.should eq '200'
  end
end

編集:タイプミスを修正


-1

セッションを非常に簡単にスタブすることもできます。

controller.session.stub(:[]).with(:user_id).and_return(<whatever user ID>)

すべてのルビー特殊演算子は確かにメソッドです。呼び出し1+1はと同じです。1.+(1)つまり+、単なるメソッドです。同様に、session[:user_id]メソッドを呼び出すと同じで[]sessionのように、session.[](:user_id)


これは合理的な解決策のようです。
超高輝度2014年

2
これはリクエスト仕様では機能しませんが、コントローラー仕様でのみ機能します。
町司2016年

-3

これはDeviseにとって非常に役立つと思いました:https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-(and-RSpec

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.