Je joue avec Rails depuis quelques années maintenant et j'ai produit deux applications passables en production. J'ai toujours évité de faire des tests et j'ai décidé de rectifier cela. J'essaie d'écrire des tests pour une application que j'ai écrite pour un travail déjà opérationnel mais en cours de révision constante. Je crains que tout changement ne casse les choses, je souhaite donc que certains tests soient opérationnels. J'ai lu le livre de RSpec, regardé quelques screencasts, mais j'ai du mal à me lancer (cela me semble être le genre de chose que vous ne comprenez que lorsque vous l'avez réellement fait).
J'essaie d'écrire ce qui devrait être un simple test de mon ReportsController. Le problème avec mon application est que pratiquement tout se trouve derrière une couche d'authentification. Rien ne fonctionne si vous n'êtes pas connecté. Je dois donc simuler un identifiant avant de pouvoir envoyer une simple demande d'obtention (bien que je suppose que je devrais écrire quelques tests pour m'assurer que rien ne fonctionne sans identifiant - je vais arriver à que plus tard).
J'ai mis en place un environnement de test avec RSpec, Capybara, FactoryGirl et Guard (je ne savais pas quels outils utiliser, par conséquent les suggestions de Railscasts utilisées). Pour ce qui est de l’écriture de mon test, j’ai jusqu’à présent créé un utilisateur dans FactoryGirl comme ceci;
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
et puis écris mon test comme tel;
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
Cela échoue comme ça;
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)>'
Il est intéressant de noter que si je modifie mon test en response.should be_redirect
, le test réussit, ce qui me suggère que tout fonctionne jusqu’à présent, mais que le nom de connexion n’est pas reconnu.
Ma question est donc qu'est-ce que je dois faire pour que cette connexion fonctionne. Dois-je créer un utilisateur dans la base de données qui corresponde aux informations d'identification de FactoryGirl? Si oui, quel est l'intérêt de FactoryGirl ici (et devrais-je même l'utiliser)? Comment puis-je créer ce faux utilisateur dans l'environnement de test? Mon système d’authentification est très simple et a été créé par moi-même (basé sur Railscasts épisode 250 ). Ce comportement de connexion devra probablement être répliqué pour presque tous mes tests, alors comment puis-je le faire une fois dans mon code et l'appliquer partout?
Je me rends compte que c’est une grande question et je vous remercie d’avoir jeté un coup d’œil.
La réponse dépend de votre implémentation d'authentification. Normalement, lorsqu'un utilisateur se connecte, vous définissez une variable de session pour mémoriser cet utilisateur, par exemple session[:user_id]
. Vos contrôleurs vont rechercher une connexion dans un before_filter
et rediriger si aucune telle variable de session n'existe. Je suppose que vous faites déjà quelque chose comme ça.
Pour que cela fonctionne dans vos tests, vous devez insérer manuellement les informations utilisateur dans la session. Voici une partie de ce que nous utilisons au travail:
# 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
Désormais, dans l’un de nos exemples de contrôleur, nous pouvons appeler login(some_user)
pour simuler la connexion sous cet utilisateur.
Je devrais également mentionner qu'il semblerait que vous effectuiez des tests d'intégration dans ce test de contrôleur. En règle générale, vos tests de contrôleur ne doivent simuler que des requêtes sur des actions individuelles du contrôleur, telles que:
it 'should be successful' do
get :index
response.should be_success
end
Ceci teste spécifiquement une action de contrôleur unique, ce que vous voulez dans un ensemble de tests de contrôleur. Ensuite, vous pouvez utiliser Capybara/Cucumber pour le test d'intégration de bout en bout des formulaires, des vues et des contrôleurs.
Ajouter un fichier d'assistance dans spec/support/controller_helpers.rb et copier le contenu ci-dessous
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
Ajoutez maintenant les lignes suivantes dans spec/Rails_helper.rb ou spec/spec_helper.rb .__ fichier
require 'support/controller_helpers'
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.include ControllerHelpers, :type => :controller
end
Maintenant dans le fichier de spécifications de votre contrôleur.
describe "GET #index" do
before :each do
@user=create(:user)
sign_in @user
end
...
end
Comme je ne pouvais pas faire fonctionner la réponse de @ Brandan, mais sur cette base et sur cet article , je suis parvenu à cette solution:
# 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
Et
# 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
Ensuite, sur vos tests, vous pouvez utiliser:
it "works" do
login_as(FactoryGirl.create(:user))
expect(request.session[:user_id]).not_to be_nil
end
Le moyen le plus simple de se connecter à un utilisateur lors de tests de fonctionnalité consiste à utiliser le helper du gardien #login_as
login_as some_user