Reputation: 266
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.
Upvotes: 1
Views: 3417
Reputation: 188
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. :)
Upvotes: 3
Reputation: 10825
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))
Upvotes: 2