According to this from the devise wiki I should be able to use a login_user helper method in my controller tests. Accordingly I have the following within the spec directory:
#spec_helper.rb
...
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
...
and
#support/controller_macros.rb
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = Factory.create(:user)
sign_in #user
end
end
end
however calling the helper doesn't work:
#requests/some_spec.rb
require 'spec_helper'
describe "GET /guides/edit" do
login_user
end
Can someone point toward where I'm going wrong. The test suite works about from this. I get a undefined local variable or method message so I guess the module isn't being included.
Rails 3.0.7
rspec 2.6.0
devise 1.3.4
backtrace
I imagine there are a couple of problems with this approach. First is that you're using request specs, not controller specs, so the login_user method is not made available by config.extend ControllerMacros, :type => :controller. Second, even if you are able to include the method it most likely won't work anyway, since the Devise test helpers are specifically written for controller/view tests, not integration tests.
Take a look at David Chelimsky's answer to this SO question, which may be of help.
I can't answer for sure... but the code smell for me is the "before(:each)" defined inside the helper. why don't you try:
#support/controller_macros.rb
module ControllerMacros
def login_user
#request.env["devise.mapping"] = Devise.mappings[:user]
#user = Factory.create(:user)
sign_in #user
end
end
and
#requests/some_spec.rb
require 'spec_helper'
describe "GET /guides/edit" do
before(:each) do
login_user
end
end
and if that fails - maybe it just can't find #request - in which case, pass it as a variable to login_user
Edit:
Looks like you might need to include the devise test helpers.
The rdoc says you should have this file:
# spec/support/devise.rb
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
Not sure if that differs from how you've already got it in spec_helper.rb
... looks pretty similar to me.
I have same issue with Rails 3.0.10 rspec 2.6.0 devise 1.3.4 spork-0.9.0.rc9 on my controller specs, i have changed config. extend to config.include and its work !
Forget to confirm if your app is not confirmable. Your code should look like
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
#user.confirm! # or set a confirmed_at inside the factory. Only necessary if you are using the confirmable module
sign_in user
end
end
end
Related
I have a controller action which uses devise's "user_signed_in?" as a condition. The action looks like this:
def show
if user_signed_in?
#do stuff
end
end
But when I am testing this action in RSpec, the tests are failing because the code inside the if block never gets executed. Is there any way to stub the "user_signed_in?" method?
You could mock user_signed_in? method like this:
before do
allow_any_instance_of(Devise::Controllers::Helpers).to receive(:user_signed_in?).and_return(false)
end
The code is not executed because the user is probably not signed in.
To sign in a devise user in rspec, have this in spec/support/controller_macros.rb.
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = FactoryGirl.create(:user)
user.confirm! #if you are using the "confirmable" module
sign_in user
end
end
end
In spec/rails_helper.rb have this:
RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
end
Then in your controller spec:
describe MyController do
login_user
....
end
I'm using Rails 5, and Devise 3.5.1.
Going through a nice (older) book about creating/testing an API, which uses Devise authentication. It was written before Rails 5, so I chose not to use the new api-only version.
Here's my test...
#/spec/controllers/api/v1/users_controller_spec.rb
require 'rails_helper'
describe Api::V1::UsersController, :type => :controller do
before(:each) { request.headers['Accept'] = "application/vnd.marketplace.v1" }
describe "GET #show" do
before(:each) do
#user = FactoryGirl.create :user
get :show, params: {id: #user.id}, format: :json
end
it "returns the information about a reporter on a hash" do
user_response = JSON.parse(response.body, symbolize_names: true)
expect(user_response[:email]).to eql #user.email
end
it { should respond_with 200 }
end
end
And here's a completely unexpected RSpec error
Devise::MissingWarden:
Devise could not find the `Warden::Proxy` instance on your request environment.
Make sure that your application is loading Devise and Warden as expected and that the `Warden::Manager` middleware is present in your middleware stack.
If you are seeing this on one of your tests, ensure that your tests are either executing the Rails middleware stack or that your tests are using the `Devise::Test::ControllerHelpers` module to inject the `request.env['warden']` object for you.
So I go here - http://www.rubydoc.info/gems/devise/Devise/Test/ControllerHelpers
and tried this -> include Devise::Test::ControllerHelpers
which didn't help because the file controller_helpers.rb is nowhere in my project
What did I miss here?
Thanks
You could add the following to your rails_helper:
RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, type: :controller
end
This will include Devise::Test::ControllerHelpers module in all :controller specs.
In the spec_helper.rb add:
config.include Devise::Test::ControllerHelpers, :type => :controller
MiniTest, Rails 4
This works for vanilla Rails 4 MiniTest
test\controllers\users_controller_test.rb
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
include Warden::Test::Helpers
include Devise::Test::ControllerHelpers
setup do
# https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara
# #user = users(:admin)
# sign_in #user
end
teardown do
Warden.test_reset!
end
test "login as admin" do
#user = users :admin
sign_in #user
get :dashboard
assert_redirected_to admin_dashboard_path
end
end
I would like to add a module that includes a method to help me log in as a user. Here it is:
module TestHelper
require 'spec_helper'
ALL_USERS = ["administrator", "instructor", "regular_user"]
def self.login_as(user_type)
user = User.find_by(global_role_id: GlobalRole.send(user_type))
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in user
end
end
The spec that's calling it is
require 'spec_helper'
RSpec.describe QuestionsController, :type => :controller do
include Devise::TestHelpers
include TestHelper
describe "a test" do
it "works!" do
TestHelper.login_as("administrator")
end
end
end
And here is the spec_helper
RSpec.configure do |config|
config.include TestHelper, :type => :controller
The error I get is: undefined method 'env' for nil:NilClass It looks like my module doesn't have access to #request.
My question is: how do I access and #request in the external Module?
In addition to the other answers, you should consider only including this module for relevant spec types using the following code:
config.include TestHelper, type: :controller # Or whatever spec type(s) you're using
You could pass #request in to TestHelper.login_as from the tests. Eg
module TestHelper
def self.login_as(user_type, request)
...
request.env['devise.mapping'] = Devise.mappings[:user]
...
end
end
...
describe 'log in' do
it 'works!' do
TestHelper.login_as('administrator', #request)
end
end
But probably better to follow the macro pattern from the devise wiki if other devise mappings in play.
With rails 4.2.0 and the latest version of RSpec I generated a controller test.
How would I ensure my admin user is logged in?
For example: if current_user.admin?
In the rspec test it mentions it like so:
let(:valid_session) { {} }
How would I enter a valid session?
First you need to add the devise helpers in spec_helper file to be accessible in the tests, as mentioned in the wiki
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
Then in the controller you could easily create a user object and sign it in using sign_in helper method
Step 1:
You can create custom methods like following in spec folder and then simply use them (after you have done what #Mohammad AbuShady's answer states which usually is done by default in rspec)
module ControllerMacros
def login_admin
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:admin]
sign_in FactoryBot.create(:user, admin:true)
end
end
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in FactoryBot.create(:user)
end
end
end
Step 2:
Add login_user or login_admin to your spec file wherever you need and change
let(:valid_session) { {} }
to
let(:valid_session) { {"warden.user.user.key" => session["warden.user.user.key"]} }
I hope you are using devise and warden which are really useful if you don't want to worry about session/login/signup issues.
You can see their documentations here:
plataformatec/devise
wardencommunity/warden
This answer was written based on documentation of devise:
devise-wiki-how-to-test
I try to test devise user authentication, the problem I've done everything according to samples, however the code still doesn't work.
spec/support/devise/devise_support.rb
module ValidUserRequestHelper
def sign_in_as_a_valid_user
#user ||= Fabricate(:simple_user)
post_via_redirect user_session_path, 'user[email]' => #user.email, 'user[password]' => #user.password
end
end
RSpec.configure do |config|
config.include ValidUserRequestHelper, :type => :request
end
spec/spec_helper.rb
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
However when I run test, it fails on the calling to `sign_in_as_a_valid_user'
undefined local variable or method `sign_in_as_a_valid_user' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1:0xc57a0c4>
I don't have idea how to debug this.
The test code is
require 'spec_helper'
describe User do
before do
sign_in_as_a_valid_user
end
...
when you write this in your rspec configuration
RSpec.configure do |config|
config.include ValidUserRequestHelper, :type => :request
end
you tell rspec to only include this helper for request spec. those are typically located in spec/request. deriving from the example of your spec that has describe User in it, i assume that you are writing a model spec, typically located in spec/model. so when running the spec, rspec won't include it for that spec!
if you just remove the :type => :request it will get included everywhere. keep in mind, that there is usually a good reason for this kind of restrictions. for example a helper that only works with a fake browser, like it is done in request specs.