ошибка проверки контроллера rspec неопределенный метод для nil:NilClass
-
20-12-2019 - |
Вопрос
Я новичок в Rspec и пытаюсь разобраться во всем мышлении BDD, поэтому я довольно озадачен этой ошибкой.У меня есть движок have rails, который я пытаюсь протестировать.Вот диспетчер бюллетеней.В принципе, перед любым действием я хочу заполнить список курсов.
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
У контроллера приложения есть несколько методов, которые я хочу запускать при каждом запросе.Я использую devise в главном приложении, поэтому у меня есть доступ к методу current_user
class ApplicationController < ::ApplicationController
before_filter :get_user
...
def get_user
@user = current_user
end
...
end
И вот спецификация, которую я пытаюсь запустить:
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
Когда я пытаюсь запустить спецификацию, я получаю сообщение об ошибке:
NoMethodError: undefined method 'id' for nil:NilClass
Я понимаю, что получаю это потому, что @user не определен, когда он вызывается в здании бюллетеня;однако я думал, что блок before в спецификации определит переменную @user после отключения фильтра :get_user.Когда я тестирую фабрики в консоли, кажется, что все создано с соответствующими ассоциациями (бюллетень -> автор, бюллетень -> курс и т.д.).
Я не уверен, чего мне не хватает относительно того, почему переменная @user не переносится в мой код контроллера.Мы были бы очень признательны за любую информацию и / или хорошие учебные пособия для rspec.
Решение
Я думаю, вам также нужно заглушить current_user
и этого будет достаточно (не нужно заглушки get_user
):
before { controller.stub(:current_user).and_return (@user = create(:user)) }
И я думаю, что хорошей практикой является позволить пользователю (если вам это нужно более одного раза):
routes { MyEngine::Engine.routes }
let!(:user) { create(:user) }
before { controller.stub(:current_user).and_return user }
Если вам нужен доступ к закрытым методам, вы можете попробовать что-то вроде этого:
subject.send(:current_user=, user)
Могло бы быть controller
вместо subject
, не уверен, какую версию поддерживает.
Обновление.На самом деле, тестировать частные методы действительно сложно.Я проверил, что current_user
в devise
определяет как:
def current_#{mapping}
@current_#{mapping} ||= warden.authenticate(scope: :#{mapping})
end
Итак, вы можете попробовать stub warden.authenticate
к возвратам user
:
allow_any_instance_of(Warden).to receive(:authenticate).and_return(create(:user))
Другие советы
Попытка устанавливать методы, которые могут быть использованы, будет довольно сложным, если вы не поймете, насколько разрабатываются.
Рекомендовать способ тестирования - просто подписать пользователь, используя Devise Test Helper в соответствии с их документацией: 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
.
Таким образом, вам не придется заботиться о разработке методов и окуривать его.Просто сосредоточиться на тестировании вашего собственного метода.:)