web-dev-qa-db-de.com

Wie simuliere ich ein Login mit RSpec?

Ich spiele seit einigen Jahren mit Rails und habe ein paar passable Apps produziert, die in Produktion sind. Ich habe es jedoch 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 die Arbeit geschrieben habe, die bereits läuft und laufend überarbeitet wird. Ich mache mir Sorgen, dass jede Änderung Dinge kaputt machen wird, daher möchte ich ein paar Tests durchführen. Ich habe das RSpec-Buch gelesen, ein paar Screencasts angeschaut, aber ich habe Schwierigkeiten, damit anzufangen (es erscheint mir als das, was man erst versteht, wenn man es tatsächlich gemacht hat).

Ich versuche zu schreiben, was ein einfacher Test meines ReportsControllers sein sollte. Das Problem bei meiner App ist, dass so ziemlich alles hinter einer Authentifizierungsebene liegt. Nichts funktioniert, wenn Sie nicht angemeldet sind, also muss ich ein Login simulieren, bevor ich sogar eine einfache Get-Anfrage senden kann (obwohl ich denke, ich sollte einige Tests schreiben, 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 mir nicht sicher, welche Tools verwendet werden sollten, um die von Railscasts empfohlenen Vorschläge zu verwenden). 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 schreibe dann 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

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

Wenn ich meinen Test in response.should be_redirect ändere, ist es interessant, dass der Test bestanden wird. Dies weist darauf hin, dass bis zu diesem Punkt alles funktioniert, der Login jedoch nicht erkannt wird.

Meine Frage ist also, was muss ich tun, damit dieses Login funktioniert. Muss ich einen Benutzer in der Datenbank erstellen, der den FactoryGirl-Anmeldedaten entspricht? Wenn ja, was macht FactoryGirl hier (und sollte ich es überhaupt verwenden)? Wie gehe ich vor, um diesen gefälschten Benutzer in der Testumgebung zu erstellen? Mein Authentifizierungssystem ist ein sehr einfaches, selbst erstelltes (basierend auf Railscasts Episode 250 ). Dieses Login-Verhalten muss vermutlich für fast alle meine Tests repliziert werden. Wie mache ich es dann, wenn ich es einmal in meinem Code mache und es überall anwenden werde?

Mir ist klar, dass dies eine große Frage ist, also danke ich Ihnen für Ihren Blick. 

49
brad

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 prüfen, ob ein Login in einem before_filter vorhanden ist, und leiten um, wenn keine solche Sitzungsvariable vorhanden ist. Ich gehe davon aus, dass du sowas schon machst.

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

# 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

In einem unserer Controller-Beispiele können wir jetzt login(some_user) aufrufen, um die Anmeldung als dieser Benutzer zu simulieren.


Ich sollte auch erwähnen, dass es so aussieht, als würden Sie in diesem Controller-Test Integrationstests durchführen. In der Regel sollten Ihre Controller-Tests nur Anforderungen an einzelne Controller-Aktionen simulieren, z.

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

Dies testet speziell eine einzelne Controller-Aktion, die Sie in einer Reihe von Controller-Tests wünschen. Anschließend können Sie Capybara/Cucumber für End-to-End-Integrationstests von Formularen, Ansichten und Controllern verwenden.

42
Brandan

Fügen Sie die Hilfedatei in spec/support/controller_helpers.rb hinzu und kopieren Sie den Inhalt unten

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

Fügen Sie nun folgende Zeilen in spec/Rails_helper.rb oder spec/spec_helper.rb .__ hinzu. Datei

require 'support/controller_helpers'

RSpec.configure do |config|

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

  end

Jetzt in Ihrer Controller-Spezifikationsdatei.

describe  "GET #index" do

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

Offiziellen Link erstellen

8

Da ich @ Brandans Antwort nicht erstellen konnte, aber basierend darauf und auf diesem Beitrag , bin ich zu dieser Lösung gekommen:

# 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 können Sie bei Ihren Tests Folgendes verwenden:

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

Die einfachste Möglichkeit, sich bei Funktionstests mit einem Benutzer anzumelden, besteht darin, den Helfer des Warden #login_as zu verwenden.

login_as some_user
0
MegaTux