Erreur de test du contrôleur rspec, méthode non définie pour nil: NilClass
-
20-12-2019 - |
Question
Je suis nouveau sur Rspec et j'essaie d'entrer dans l'ensemble de l'état d'esprit BDD, donc je suis assez perplexe face à cette erreur.J'ai un moteur rails que j'essaie de tester.Voici le contrôleur de bulletin.Fondamentalement, avant toute action, je souhaite remplir la liste des cours.
class BulletinsController < ApplicationController
before_filter :get_courses
def new
@bulletin = Bulletin.new(author_id: @user.id)
end
...
private
def get_courses
if @user.has_role? :admin
@course_list = Course.all.sort_by(&:start_date)
...
end
end
Le contrôleur d'application dispose de certaines méthodes que je souhaite exécuter à chaque requête.J'utilise Devise dans l'application hôte, j'ai donc accès à la méthode current_user
class ApplicationController < ::ApplicationController
before_filter :get_user
...
def get_user
@user = current_user
end
...
end
Et voici la spécification que j'essaie d'exécuter :
describe BulletinsController do
routes { MyEngine::Engine.routes }
before { controller.stub(:authenticate_user!).and_return true }
before { controller.stub(:get_user).and_return (@user = create(:user)) }
describe "GET #new" do
it "assigns a new bulletin to @bulletin" do
bulletin = create(:bulletin)
controller.stub(:get_courses)
get :new
assigns(:bulletin).should eq(bulletin)
end
end
end
Lorsque j'essaie d'exécuter la spécification, j'obtiens l'erreur :
NoMethodError: undefined method 'id' for nil:NilClass
Je comprends que je reçois cela parce que @user n'est pas défini lorsqu'il est appelé dans la création du bulletin ;cependant, je pensais que le bloc before de la spécification définirait la variable @user après avoir supprimé le filtre :get_user.Lorsque je teste les usines dans la console, tout semble être créé avec les bonnes associations (bulletin -> auteur, bulletin -> cours, etc).
Je ne suis pas sûr de ce qui me manque quant à la raison pour laquelle la variable @user n'est pas transmise au code de mon contrôleur.Tout aperçu et/ou bon tutoriel pour rspec serait grandement apprécié.
La solution
Je suppose que tu dois aussi faire un stub current_user
et ce sera suffisant (pas besoin de stupéfier get_user
):
before { controller.stub(:current_user).and_return (@user = create(:user)) }
Et je suppose que la bonne pratique est de laisser l'utilisateur (si vous en avez besoin plus d'une fois) :
routes { MyEngine::Engine.routes }
let!(:user) { create(:user) }
before { controller.stub(:current_user).and_return user }
Si vous avez besoin d'un accès aux méthodes privées, vous pouvez essayer quelque chose comme ceci :
subject.send(:current_user=, user)
Cela pourrait être un controller
au lieu de subject
, je ne sais pas quelle version est prise en charge.
Mise à jour.En fait, c'est vraiment délicat de tester des méthodes privées.j'ai vérifié ça current_user
dans devise
définit comme :
def current_#{mapping}
@current_#{mapping} ||= warden.authenticate(scope: :#{mapping})
end
Alors, vous pouvez essayer le stub warden.authenticate
aux retours user
:
allow_any_instance_of(Warden).to receive(:authenticate).and_return(create(:user))
Autres conseils
Essayer d'encercler les méthodes que le concevoir pourrait utiliser sera assez difficile à moins que vous compreniez à quel point le développement fonctionne.
Le moyen recommandé de tester consiste simplement à vous connecter à l'utilisateur à l'aide de Devise Test Helper conformément à leur documentation: https://github.com/plataformatec/devise#test-HelPers
describe BulletinsController do
routes { MyEngine::Engine.routes }
before { sign_in(user) }
let!(:user) { create(:user) }
describe "GET #new" do
it "assigns a new bulletin to @bulletin" do
bulletin = create(:bulletin)
controller.stub(:get_courses)
get :new
assigns(:bulletin).should eq(bulletin)
end
end
end
De cette façon, vous n'aurez pas à vous soucier de concevoir des méthodes et de l'emporter.Concentrez-vous simplement sur le test de votre propre méthode.:)