2012-04-12 6 views
39

Ho giocato con Rails per un paio d'anni e ho prodotto un paio di app passabili che sono in produzione. Ho sempre evitato di fare test e ho deciso di correggerlo. Sto provando a scrivere alcuni test per un'app che ho scritto per il lavoro che è già attivo e funzionante ma in costante revisione. Sono preoccupato del fatto che eventuali modifiche interromperanno le cose, quindi voglio avere alcuni test attivi e funzionanti. Ho letto il libro di RSpec, ho guardato alcuni screencast ma sto faticando per iniziare (mi sembra il tipo di cosa che capisci solo dopo averlo fatto).Come si simula un accesso con RSpec?

Sto provando a scrivere quello che dovrebbe essere un semplice test del mio ReportsController. Il problema con la mia app è che praticamente l'intera cosa si trova dietro un livello di autenticazione. Nulla funziona se non hai effettuato l'accesso, quindi devo simulare un login prima di poter inviare anche una semplice richiesta get (anche se penso che dovrei scrivere alcuni test per assicurarmi che nulla funzioni senza un login - Arriverò a più tardi).

Ho installato un ambiente di test con RSpec, Capybara, FactoryGirl e Guard (non ero sicuro di quali strumenti utilizzare i suggerimenti di Railscast così utilizzati). Il modo in cui sono andato a scrivere il mio test finora è creare un utente in FactoryGirl in questo modo;

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 

e quindi scrivere il mio test in questo modo;

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 

Questo non funziona così;

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

È interessante notare che se cambio il mio test per response.should be_redirect, il test viene superato, che mi fa pensare che tutto funzioni fino a quel punto, ma il login non viene riconosciuta.

Quindi la mia domanda è cosa devo fare per far funzionare questo accesso. Devo creare un utente nel database che corrisponda alle credenziali di FactoryGirl? Se è così, qual è il punto di FactoryGirl qui (e dovrei anche usarlo)? Come faccio a creare questo falso utente nell'ambiente di test? Il mio sistema di autenticazione è un self-made molto semplice (basato su Railscasts episode 250). Presumibilmente questo comportamento di registrazione dovrebbe essere replicato per quasi tutti i miei test, quindi come faccio a farlo una volta nel mio codice e applicandolo ovunque?

Mi rendo conto che questa è una grande domanda, quindi ti ringrazio per aver dato un'occhiata.

risposta

36

La risposta dipende dall'implementazione dell'autenticazione. Normalmente, quando un utente accede, imposterai una variabile di sessione per ricordare quell'utente, qualcosa come session[:user_id]. I controllori verificheranno il login in un before_filter e il reindirizzamento se non esiste alcuna variabile di sessione del genere. Presumo che tu stia già facendo qualcosa del genere.

Per ottenere questo risultato nei test, è necessario inserire manualmente le informazioni utente nella sessione. Ecco una parte di ciò che usiamo al lavoro:

# 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 

Ora, in uno qualsiasi dei nostri esempi di controller, possiamo chiamare login(some_user) per simulare il login come quell'utente.


Vorrei anche menzionare che sembra che tu stia facendo test di integrazione in questo test del controller.Come regola generale, i test del controller devono essere simulando solo le richieste alle singole azioni di controllo, come:

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

Questo test specificamente una sola azione di controllo, che è ciò che si desidera in una serie di test di controller. Quindi è possibile utilizzare Capybara/Cucumber per test di integrazione end-to-end di moduli, viste e controllori.

+1

Grande, grazie. Questa è una spinta enorme nella giusta direzione. L'ho messo in pratica e ho ancora un test fallito perché non ho impostato nessun utente nell'ambiente di test. Come dovrei andare a configurarli? Usa FactoryGirl? Creali nella console con un ambiente di test? Copia su un database di produzione esistente nell'ambiente di test? – brad

+0

In realtà, non preoccuparti. Sono riuscito a farlo con FactoryGirl in questo modo; 'descrive" GET 'index' "do; "dovrebbe avere successo" fare; user = FactoryGirl.create (: user); login (utente); ottieni: indice; response.should be_success; fine; fine; '. Ho dovuto cambiare 'request.session [: user]' a 'request.session [: user_id]' ma, a parte questo, tutto nel tuo codice ha funzionato per me e ora ho il mio primo test di passaggio! Grazie. – brad

+0

Sì, la soluzione che utilizza FactoryGirl per creare l'utente è essenzialmente ciò che facciamo anche noi. Assicurati di accettare la risposta se funziona per te. Grazie! – Brandan

1

Come non ho potuto fare il lavoro @ risposta di Brandan, ma sulla base di esso e su this post, ho venuto a questa soluzione:

# 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 

E

# 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 

Poi, il tuo test è possibile utilizzare: File

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

Add aiutante in spec/supporto/controlle r_helpers.rb e copiare il contenuto di sotto

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 

Ora aggiungere le righe seguenti in spec/rails_helper.rb o spec/spec_helper.rb file di

require 'support/controller_helpers' 

RSpec.configure do |config| 

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

    end 

Ora nel file spec del controller .

describe "GET #index" do 

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

Devise Official Link

+0

Non so perché la tua risposta abbia ottenuto "-1" perché quello è l'UNICO che in realtà ha funzionato per me anche se sembra un po '"disordinato"; grazie mille amico;) – Laurent

+0

grazie a @Laurent. Sono contento che abbia aiutato. (y) –

+0

Prima di tutto, 'metodo non definito create'. Se la persona doveva cambiare 'create' con qualcos'altro, allora perché non dirlo? Devo sostituire anche 'describe' o' sign_in' in qualcos'altro? questo pseudo-codice è confuso. –

0

Il modo più semplice per effettuare il login con un utente su prove di funzionalità è quello di utilizzare il Warden helper #login_as

login_as some_user 
Problemi correlati