Question

I'm new to Rspec and I am trying to get into the whole BDD mindset, so I'm pretty stumped about this error. I have have rails engine that I am trying to test. Here is the bulletin controller. Basically before any action I want to populate the list of courses.

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

The application controller has some methods that I want run on each request. I am using devise in the host app so I have access to the current_user method

class ApplicationController < ::ApplicationController
  before_filter :get_user
  ...
  def get_user
    @user = current_user
  end
  ...
end

And here is the spec I am trying to run:

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

When I try to run the spec, I get the error:

NoMethodError: undefined method 'id' for nil:NilClass

I understand that I am getting this because @user is not defined when it is called in the bulletin building; however I thought that the before block in the spec would define the @user variable after stubbing out the :get_user filter. When I test the factories in the console, everything seems to be created with the proper associations (bulletin -> author, bulletin -> course, etc).

I'm not sure what I'm missing as to why the @user variable is not being carried through to my controller code. Any insight and/or good tutorials for rspec would be greatly appreciated.

Was it helpful?

Solution

I guess You also need to stub current_user and it will be enough (no need to stub get_user):

before { controller.stub(:current_user).and_return (@user = create(:user)) }

And i guess the good practice is to let user (if you need it more than once):

routes { MyEngine::Engine.routes }
let!(:user) { create(:user) }
before { controller.stub(:current_user).and_return user }

If you need an access to private methods, you can try something like this:

subject.send(:current_user=, user)

Could be a controller instead of subject, not sure what version which supports.

Update. Actually, it's really tricky to test private methods. I checked that current_user in devise defines like:

def current_#{mapping}
  @current_#{mapping} ||= warden.authenticate(scope: :#{mapping})
end

So, you can try stub warden.authenticate to returns user:

allow_any_instance_of(Warden).to receive(:authenticate).and_return(create(:user))

OTHER TIPS

Trying to stub out the methods that Devise could be using will be quite difficult unless you understand how Devise works.

The recommend way to test is to simply sign in the user using Devise test helper as per their 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

This way, you won't have to care about Devise methods and stubbing it. Just focus on testing your own method. :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top