2011-04-26 9 views
76

Podczas pisania specyfikacji żądania, jak ustawić sesje i/lub metody kontrolerów stacyjnych? Próbuję skrótową z uwierzytelniania w moich testów integracyjnych - rspec/żądaAutoryzacja zaszywania na żądanie Spec

Oto przykład testu

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 

Pomocnik:

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 

A ApplicationController który obsługuje uwierzytelnianie :

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 

Dlaczego nie jest to możliwe? ble, aby uzyskać dostęp do tych zasobów?

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

Odpowiedz

97

Żądanie spec cienką owinięcie wokół ActionDispatch::IntegrationTest, które nie działają jak specyfikacje kontrolera (które owijane ActionController::TestCase). Mimo że dostępna jest metoda sesji, nie sądzę, że jest ona obsługiwana (tj. Prawdopodobnie jest tam, ponieważ moduł dołączany do innych narzędzi również zawiera tę metodę).

Polecam zalogować się, publikując dowolną akcję, której używasz do uwierzytelniania użytkowników. Jeśli się hasło „password” (na przykład) dla wszystkich fabryk użytkownika, a następnie można zrobić coś takiego:

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

Dzięki David. Działa świetnie, ale wydaje się, że jest trochę przesadą, wykonując wszystkie te prośby? –

+17

Gdybym myślał, że to przesada, nie poleciłbym tego :) –

+4

To także najprostszy sposób, aby zrobić to niezawodnie. 'ActionDispatch :: IntegrationTest' ma symulować jednego lub więcej użytkowników wchodzących w interakcje za pośrednictwem przeglądarki, bez konieczności korzystania z prawdziwych przeglądarek. Istnieje potencjalnie więcej niż jeden użytkownik (tj.sesja) i więcej niż jeden kontroler w jednym przykładzie, a obiekty sesja/kontroler są tymi używanymi w ostatnim żądaniu. Nie masz do nich dostępu przed żądaniem. –

58

BTW odpowiedź @David Chelimsky może potrzebować trochę szczypanie jeśli używasz opracować . Co robię w moim testów integracyjnych/wnioski (dzięki 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

kiedy używam 'login user1' w specyfikacji modelu rspec, otrzymuję niezdefiniowaną zmienną lokalną lub metodę 'user_session_path' dla # jpwynn

+1

Zakłada to mieć 'devise_for: users' w pliku' config/routes.rb'. Jeśli określiłeś coś innego, musisz odpowiednio dostosować swój kod. –

+0

To zadziałało dla mnie, musiałem go nieco zmodyfikować. Zmieniłem ''user [email]' => user.email' na' 'user [nazwa użytkownika]' => user.username', ponieważ moja aplikacja używa nazwy użytkownika jako loginu zamiast e-maila. – webdevguy

2

FWIW w przenoszeniu moich testów test :: urządzenie do RSpec, chciałem być w stanie zalogować się z wielu (opracowania jak sesje) w moim wniosku specyfikacje. Minęło trochę kopania, ale udało mi się to dla mnie. Używanie Rails 3.2.13 i 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 

I ...

# 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 

Edit fixed typo

-1

Można dość łatwo skrótową sesję, jak również.

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

Wszystkie specjalne operatory ruby ​​są rzeczywiście metodami. Wywołanie 1+1 jest takie samo jak 1.+(1), co oznacza, że ​​+ to tylko metoda. Podobnie session[:user_id] jest taka sama jak wywołanie metody [] na session, jak session.[](:user_id)

+0

To wydaje się rozsądnym rozwiązaniem. – superluminary

+1

To nie działa w specyfikacji żądania, ale tylko w specyfikacji kontrolera. – Machisuji