2012-04-12 13 views
39

Ich spiele jetzt seit ein paar Jahren mit Rails und habe ein paar bearbeitbare Apps produziert, die in Produktion sind. Ich habe es immer vermieden, Tests durchzuführen, und ich habe beschlossen, das zu korrigieren. Ich versuche, einige Tests für eine App zu schreiben, die ich für eine Arbeit geschrieben habe, die bereits läuft, aber ständig überarbeitet wird. Ich bin besorgt, dass irgendwelche Änderungen die Dinge brechen werden, also möchte ich einige Tests starten und laufen. Ich habe das RSpec-Buch gelesen, ein paar Screencasts gesehen, aber ich habe Mühe, loszulegen (es fällt mir so auf, wie du es nur verstehst, wenn du es erst einmal getan hast).Wie simuliere ich eine Anmeldung mit RSpec?

Ich versuche zu schreiben, was ein einfacher Test meines ReportsController sein sollte. Das Problem mit meiner App ist, dass das ganze Ding hinter einer Authentifizierungsschicht sitzt. Nichts funktioniert, wenn Sie nicht eingeloggt sind, also muss ich eine Anmeldung simulieren, bevor ich eine einfache Anfrage senden kann (obwohl ich denke, dass ich einige Tests schreiben sollte, um sicherzustellen, dass nichts ohne Login funktioniert - ich komme dazu das später).

Ich habe eine Testumgebung mit RSpec, Capybara, FactoryGirl und Guard eingerichtet (war nicht sicher, welche Tools zu verwenden sind, so dass die Vorschläge von Railscasts verwendet wurden). Die Art, wie ich meinen Test bisher geschrieben habe, besteht darin, einen Benutzer in FactoryGirl zu erstellen;

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 

und dann schreibe meinen Test so;

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 

Dies funktioniert nicht so;

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

Interessanter wenn ich response.should be_redirect meinen Test ändern, hat der Test bestanden, die ich zeigt, dass alles, was bis zu diesem Punkt Aufarbeiten wird, aber die Login nicht erkannt wird.

Also meine Frage ist, was ich tun muss, um dieses Login funktionieren zu lassen. Muss ich einen Benutzer in der Datenbank erstellen, der den FactoryGirl-Anmeldeinformationen entspricht? Wenn ja, worauf kommt FactoryGirl hier hinaus (und sollte ich es sogar verwenden)? Wie gehe ich vor, um diesen gefälschten Benutzer in der Testumgebung zu erstellen? Mein Authentifizierungssystem ist ein sehr einfaches selbstgemachtes System (basierend auf Railscasts episode 250). Dieses Anmeldeverhalten wird vermutlich für fast alle meine Tests repliziert werden müssen. Wie gehe ich also vor, es einmal in meinem Code zu tun und überall anzuwenden?

Ich weiß, das ist eine große Frage, also danke ich Ihnen für einen Blick.

Antwort

36

Die Antwort hängt von Ihrer Authentifizierungsimplementierung ab. Wenn sich ein Benutzer anmeldet, legen Sie normalerweise eine Sitzungsvariable fest, um sich an diesen Benutzer zu erinnern, etwa session[:user_id]. Ihre Controller werden nach einer Anmeldung in einer before_filter suchen und umleiten, wenn keine solche Sitzungsvariable existiert. Ich nehme an, du machst schon so etwas.

Damit dies in Ihren Tests funktioniert, müssen Sie die Benutzerinformationen manuell in die Sitzung einfügen. Hier ist ein Teil dessen, was wir verwenden bei der Arbeit:

# 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 

nun in einem unserer Controller Beispiele können wir login(some_user) nennen als dieser Benutzer anmelden zu simulieren.


Ich sollte auch erwähnen, dass es aussieht, als ob Sie Integrationstests in diesem Controller-Test machen.In der Regel sollen Sie Ihre Controller-Tests nur Anfragen an einzelnen Controller-Aktionen werden simuliert, wie:

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

Diese Tests, die speziell eine einzelne Controller-Aktion, das ist, was Sie in einer Reihe von Controller-Tests wollen. Dann können Sie Capybara/Cucumber für die End-to-End-Integrationsprüfung von Formularen, Ansichten und Controllern verwenden.

+1

Große, dank des Wächters zu verwenden. Das ist ein großer Schritt in die richtige Richtung. Ich habe dies in die Praxis umgesetzt und habe immer noch einen fehlgeschlagenen Test, da ich in der Testumgebung keine Benutzer eingerichtet habe. Wie sollte ich sie einrichten? Verwenden Sie FactoryGirl? Erstellen Sie sie in der Konsole mit einer Testumgebung? Über eine vorhandene Produktionsdatenbank in die Testumgebung kopieren? – brad

+0

Eigentlich keine Sorge. Ich habe es mit FactoryGirl so gemacht; 'beschreiben" GET 'index' "do; es "sollte erfolgreich sein" tun; Benutzer = FactoryGirl.create (: Benutzer); Login (Benutzer); erhalten: Index; response.should be_success; Ende; Ende; '. Ich musste 'request.session [: user]' in 'request.session [: user_id]' ändern, aber abgesehen davon funktionierte alles in Ihrem Code für mich und ich habe jetzt meinen ersten bestandenen Test! Vielen Dank. – brad

+0

Ja, Ihre Lösung mit FactoryGirl zur Erstellung des Benutzers ist im Wesentlichen das, was wir auch tun. Bitte achten Sie darauf, die Antwort zu akzeptieren, wenn es für Sie funktioniert. Vielen Dank! – Brandan

1

Da ich nicht @ Brandan Antwort Arbeit machen konnte, aber auf ihr basiert und auf this post habe ich zu dieser Lösung kam:

# 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 

Und

# 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 

Dann auf Ihre Tests Sie verwenden können:

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

Add-Helfer-Datei in spec/support/controlle r_helpers.rb und Kopieren von Inhalten unter

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 

Nun fügen Sie folgende Zeilen in spec/rails_helper.rb oder spec/spec_helper.rb Datei

require 'support/controller_helpers' 

RSpec.configure do |config| 

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

    end 

Jetzt in Ihrem Controller-Spez-Datei .

describe "GET #index" do 

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

Devise Official Link

+0

Ich weiß nicht, warum deine Antwort "-1" hat, denn das ist der EINZIGE, der tatsächlich für mich funktioniert hat, sogar sieht es ein wenig "chaotisch" aus; vielen dank mann;) – Laurent

+0

danke @Laurent. Ich bin froh, dass es geholfen hat. (y) –

+0

Zuerst, undefined Methode erstellen. Wenn die Person "create" zu etwas anderem ändern sollte, warum nicht erwähnen? Sollte ich auch "describe" oder "sign_in" durch etwas anderes ersetzen? Dieser Pseudocode ist verwirrend. –

0

Der einfachste Weg, mit einem Benutzer auf Feature-Tests anmelden ist die helper #login_as

login_as some_user 
Verwandte Themen