rspec 控制器测试错误 nil:NilClass 的未定义方法
-
20-12-2019 - |
题
我是 Rspec 的新手,我正在尝试进入整个 BDD 思维模式,所以我对这个错误感到非常困惑。我有正在尝试测试的 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 块会在删除 :get_user 过滤器后定义 @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
所以,你可以尝试存根 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
.
这样,您将不必关心设计方法并将其存入。只关注测试自己的方法。:)