【问题标题】:rspec controller test error undefined method for nil:NilClassrspec控制器测试错误nil的未定义方法:NilClass
【发布时间】:2014-07-22 23:20:06
【问题描述】:

我是 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

应用程序控制器有一些我想在每个请求上运行的方法。我在主机应用程序中使用设计,所以我可以访问 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 的见解和/或好的教程。

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-4 rspec bdd


    【解决方案1】:

    除非您了解 Devise 的工作原理,否则尝试排除 Devise 可能使用的方法将非常困难。

    推荐的测试方法是使用 Devise 测试助手按照他们的文档简单地登录用户: 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
    

    这样,您就不必关心设计方法和存根它。只需专注于测试您自己的方法。 :)

    【讨论】:

    • 感谢您的建议。我试图避免使用 Devise 测试助手,因为 Devise 正在另一个插入主应用程序的引擎中使用。我不认为我可以访问那个测试助手,我不想在我的测试应用程序中设置设计。
    【解决方案2】:

    我猜你还需要存根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,不确定哪个版本支持。

    更新。实际上,测试私有方法确实很棘手。我检查了devise 中的current_user 定义如下:

    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))
    

    【讨论】:

    • 存根 current_user 方法有效。谢谢!另外,感谢您提供有关私有方法的额外信息
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多