2011-04-26 37 views
76

在編寫請求規範時,如何設置會話和/或存根控制器方法? 我想在我的集成測試存根出認證 - RSpec的/請求請求中的Stubbing驗證規範

這是一個測試的例子

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 

爲什麼它不是possi可以訪問這些資源嗎?

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)>' 

回答

97

請求規格是薄的包裝紙圍繞ActionDispatch::IntegrationTest,它不象控制器的規格工作(其纏繞ActionController::TestCase)。儘管有一個會話方法可用,但我認爲它不被支持(即可能存在,因爲包含在其他實用程序中的模塊也包含該方法)。

我建議通過發佈登錄到您用來驗證用戶的任何操作。如果您的密碼,「密碼」(例如)的所有用戶的工廠,那麼你可以做這樣的事情:

 
def login(user) 
    post login_path, :login => user.login, :password => 'password' 
end 
+0

感謝大衛。它工作得很好,但它似乎有點矯枉過正,使所有這些要求? – 2011-04-27 11:52:52

+17

如果我認爲這是過度殺傷,我不會推薦它:) – 2011-04-27 12:54:32

+4

這也是最可靠的方法。 'ActionDispatch :: IntegrationTest'旨在模擬一個或多個用戶通過瀏覽器進行交互,而不必使用真正的瀏覽器。可能有多個用戶(即會話)以及一個示例中的多個控制器,並且會話/控制器對象是上次請求中使用的那些對象。請求前您無權訪問它們。 – 2011-04-27 13:02:45

58

順便說一句,如果你使用的設計@大衛赫利姆斯基的答案可能需要一些調整。我在做什麼在我的集成/請求測試(感謝this StackOverflow post):

# 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模型規範中使用'login user1'時,我得到未定義的局部變量或方法'user_session_path'爲# jpwynn 2012-12-20 19:36:49

+1

這假設你在'config/routes.rb'文件中有'devise_for:users'。如果你指定了不同的東西,你必須相應地調整你的代碼。 – 2013-02-23 17:10:36

+0

這工作對我來說我不得不稍微修改它。因爲我的應用程序使用用戶名作爲登錄名而不是電子郵件,所以我將'user [email]'=> user.email'更改爲''user [username]'=> user.username'。 – webdevguy 2016-05-24 14:25:01

2

FWIW,在移植我的Test ::單元測試RSpec的,我希望能夠與多個登錄(設計)會議在我的請求規格。它採取了一些挖掘,但得到這個爲我工作。使用Rails 3.2.13和RSpec 2.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, "https://stackoverflow.com/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>) 

所有的ruby特殊運算符的確是方法。調用1+11.+(1)相同,這意味着+只是一種方法。同樣,session[:user_id]是一樣的調用方法[]session,作爲session.[](:user_id)

+0

這似乎是一個合理的解決方案。 – superluminary 2014-03-24 14:58:54

+1

這不適用於請求規範,但僅適用於控制器規範。 – Machisuji 2016-06-14 12:18:21