web-dev-qa-db-fra.com

Comment simuler une connexion avec RSpec?

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. 

49
brad

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.

42
Brandan

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

Devise Official Link

8
Taimoor Changaiz

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
2
Bruno Peres

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
0
MegaTux