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.
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.
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
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
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