When writing a request spec, how do you set sessions and/or stub controller methods? I'm trying to stub out authentication in my integration tests - rspec/requests
Here's an example of a test
require File.dirname(__FILE__) + '/../spec_helper'
require File.dirname(__FILE__) + '/authentication_helpers'
describe "Messages" do
include AuthenticationHelpers
describe "GET admin/messages" do
before(:each) do
@current_user = Factory :super_admin
login(@current_user)
end
it "displays received messages" do
sender = Factory :jonas
direct_message = Message.new(:sender_id => sender.id, :subject => "Message system.", :content => "content", :receiver_ids => [@current_user.id])
direct_message.save
get admin_messages_path
response.body.should include(direct_message.subject)
end
end
end
The helper:
module AuthenticationHelpers
def login(user)
session[:user_id] = user.id # session is nil
#controller.stub!(:current_user).and_return(user) # controller is nil
end
end
And the ApplicationController that handles authentication:
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user
helper_method :logged_in?
protected
def current_user
@current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def logged_in?
!current_user.nil?
end
end
Why is it not possible to access these resources?
1) Messages GET admin/messages displays received messages
Failure/Error: login(@current_user)
NoMethodError:
undefined method `session' for nil:NilClass
# ./spec/requests/authentication_helpers.rb:3:in `login'
# ./spec/requests/message_spec.rb:15:in `block (3 levels) in <top (required)>'
A request spec is a thin wrapper around
ActionDispatch::IntegrationTest
, which doesn't work like controller specs (which wrapActionController::TestCase
). Even though there is a session method available, I don't think it is supported (i.e. it's probably there because a module that gets included for other utilities also includes that method).I'd recommend logging in by posting to whatever action you use to authenticate users. If you make the password 'password' (for example) for all the User factories, then you can do something like this:
FWIW, in porting my Test::Unit tests to RSpec, I wanted to be able to login with multiple (devise) sessions in my request specs. It took some digging, but got this to work for me. Using Rails 3.2.13 and RSpec 2.13.0.
And...
Edit: fixed typo
Note for Devise users...
BTW, @David Chelimsky's answer may need a little tweaking if you're using Devise. What I'm doing in my integration / requests testing (thanks to this StackOverflow post):
You could pretty easily stub the session as well.
All ruby special operators are indeed methods. Calling
1+1
is the same as1.+(1)
, which means+
is just a method. Similarly,session[:user_id]
is the same as calling method[]
onsession
, assession.[](:user_id)
I found this very helpful for Devise : https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-(and-RSpec)