Rails, Devise, Rspec: Undefined method 'sign_in' - ruby-on-rails-4

I am trying to write Rspec tests in Rails, using Devise helper methods for signing in and out. The sign_in method is not working. However, it had been working earlier, before a slew of changes to the app.
Things I have tried:
I am including the test helpers in Rspec.configure.
Using Warden's login_as
Clearing the Rails cache.
Getting rid of Capybara to see if that were causing the issue
I am not setting the session explicitly in my controller specs (e.g. no valid_session)
So far, no dice. What do I need to do differently to test my controllers with a signed-in user?
Error message:
OrderItemsController GET #index renders the :index view
Failure/Error: sign_in :admin
NoMethodError:
undefined method `sign_in' for # <RSpec::ExampleGroups::OrderItemsController_2::GETIndex:0x00000102c002d0>
# ./spec/controllers/order_items_controller_spec.rb:6:in `block (2 levels) in <top (required)>'
Controller Spec
require 'spec_helper'
describe OrderItemsController do
before (:each) do
admin = create(:admin)
sign_in :admin
end
describe "GET #index" do
it "renders the :index view" do
get :index
expect( response ).to render_template :index
end
end
end
spec_helper.rb
require 'rspec/rails'
require 'capybara/rspec'
RSpec.configure do |config|
config.include ApplicationHelper
config.include ControllersHelper
config.include UsersHelper
config.include Devise::TestHelpers, type: :controller
config.include FactoryGirl::Syntax::Methods
end
Gemfile
group :development, :test do
gem 'rspec-rails', '~> 3.0.0.beta'
gem 'capybara'
gem 'factory_girl_rails'
gem 'faker'
gem 'dotenv-rails'
gem 'guard'
gem 'guard-annotate'
gem 'guard-rspec', require: false
gem 'guard-livereload', require: false
gem 'foreman'
end
factories/user.rb
FactoryGirl.define do
factory :user do
first { Faker::Name.first_name }
last { Faker::Name.last_name }
email { Faker::Internet.email }
admin false
password "secrets1"
password_confirmation "secrets1"
confirmed_at Date.today
factory :admin do
admin true
end
end
end
Thanks in advance.

Did you recently upgrade to RSpec 3 like I did? This is from the RSpec 3 documentation:
Automatically Adding Metadata
RSpec versions before 3.0.0 automatically added metadata to specs based on
their location on the filesystem. This was both confusing to new users and not
desirable for some veteran users.
In RSpec 3, this behavior must be explicitly enabled:
​# spec/rails_helper.rb
RSpec.configure do |config|
config.infer_spec_type_from_file_location!
end
Since this assumed behavior is so prevalent in tutorials, the default
configuration generated by rails generate rspec:install enables this.
If you follow the above listed canonical directory structure and have
configured infer_spec_type_from_file_location!, RSpec will automatically
include the correct support functions for each type.
After I add that configuration snippet, I no longer have to specify the spec type (e.g. type: :controller).

I figured out a solution. I explicitly defined the controller's Describe block as a controller type.
describe OrderItemsController, :type => :controller do
I still don't understand why this code worked earlier but now needs this (seemingly redundant) explicit declaration. Regardless, I'd appreciate learning what happened here. Thanks!

I can provide you an example (works for me - rspec / capybara / simplecov etc..)
spec/spec_helper.rb
require 'capybara/rspec'
require 'capybara/rails'
RSpec.configure do |config|
config.use_transactional_fixtures = true
config.infer_base_class_for_anonymous_controllers = false
config.include FactoryGirl::Syntax::Methods
config.include Devise::TestHelpers, type: :controller
config.include Capybara::DSL
config.include Warden::Test::Helpers
config.include Rails.application.routes.url_helpers
end
spec/integration/user_flow_spec.rb
require 'spec_helper'
feature 'Verify contract' do
# Create employee
let(:employee) { create(:employee) }
let (:book) { create(:book) }
# Sign in employee before each test!
before :each do
login_as employee, scope: :user
end
scenario 'create book' do
# Visit Index and click to create
visit employee_books_path
click_link 'Create'
expect(current_path).to eq(employee_books_path)
end
end
I hope it will be ok :) I think your problem is missing Warden test helpers...

You can use the Devise helper in the file spec/spec_helper.rb:
RSpec.configure do |config|
config.include Devise::TestHelpers, type: :controller
end

If you need the sign_in method in a request spec file, then you should include this:
config.include Devise::Test::IntegrationHelpers, type: :request

For Rails 5 and Rspec 3 you need to add this into your spec_helper.rb
config.include Devise::Test::ControllerHelpers, type: :controller

You can use login_as method instead like this:
# rails_helper.rb
RSpec.configure do |config|
config.include Warden::Test::Helpers
end
In your spec file use:
#spec/integration/user_flow_spec.rb
before :each do
employee = create(:employee)
login_as employee, scope: :user
end

Related

rspec: Add feature specs to default spec

I've got a rails 4.2 application with rspec-rails 3.5.1
I've got feature specs in spec/features/<module>/<feature>_spec.rb
When I run
bin/rspec
or
bin/rake spec
the feature specs do not run.
They run fine if I do
bin/rspec spec/features/*/*_spec.rb
How can I include this path in the default spec?
Update
Here's one of the specs in brief
require 'rails_helper'
Rspec.describe 'User logs in', :type => :feature do
let(:user) { FactoryGirl.create(:user) }
scenario 'with valid credentials' do
... setup ...
expect(page).to have_content "Success"
end
end
I've also tried replacing Rspec.describe with describe, feature, Rspec.feature. The spec doesn't run in the default specs.

Testing custom devise session controller using RSpec

I'm using a custom Devise SessionsController:
#custom_sessions_controller.rb
class CustomSessionsController < Devise::SessionsController
end
In my routes.rb devise is set up like this:
#routes.rb
devise_for :custom_users, {
:singular => 'custom_user',
:class_name => 'CustomUser',
:path => "/",
:path_names => { :sign_in => 'login', :sign_out => 'logout' },
:controllers => { :sessions => "custom_sessions" }
}
I would like to write a simple rspec test:
#custom_sessions_controller_spec.rb
require 'rails_helper'
describe CustomSessionsController, :type => :controller do
describe "login" do
before do
setup_controller_for_warden
#request.env["devise.mapping"] = Devise.mappings[:custom_user]
#my_user = FactoryGirl.create(:custom_user) # creates user with valid credentials
end
it "should succeed with valid credentials" do
sign_in #my_user
curr_user = assigns(:current_custom_user)
expect(curr_user).to eq(#my_user)
expect(response).to be_success
end
end
end
In my rails_helper.rb the following lines are present:
require 'devise'
...
RSpec.configure do |config|
...
config.include Devise::TestHelpers, :type => :controller
config.include Warden::Test::Helpers , :type => :controller
...
The problem is that curr_user in the test is alway nil. What am I doing wrong? Or how to test a custom devise session controller? Or how to log in in - other - tests using a custom devise session?
instead of:
describe CustomSessionsController, :type => :controller do
try:
RSpec.describe CustomSessionsController, :type => :controller do
Hope it works!
Using
#controller.current_custom_user
instead of
assigns(:current_custom_user)
seems to work.

Loading and using devise macros in rspec tests

Following the instructions at https://github.com/plataformatec/devise/wiki/How-To:-Test-controllers-with-Rails-3-and-4-%28and-RSpec%29, I have set up some macros in a file spec/support/rspec_macros.rb:
module ControllerMacros
def login_admin
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in FactoryGirl.create(:user, role: 4)
end
end
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mappings[:user]
sign_in FactoryGirl.create(:user)
end
end
end
(Note that in my app, "admin" is a role -- role 4 -- that any user can be assigned to, and yes, once I get this working I'll DRY this up and get rid of the magic number).
However, putting login_admin into my spec doesn't work: it can't see login_admin.
So, following the advice at ControllerMacros cannot be seen in RSpec I add
config.include ControllerMacros, :type => :controller
to my spec_helper.rb file. Now ControllerMacros is an uninitialized constant, so following the advice at rspec not working with devise user authentication I add:
require 'support/controller_macros'
to my spec_helper file, and I find that I'm back where I started with undefined local variable or method login_admin
Help! How do I get to the macro? And is there one single place where the whole process is documented?
For info, the relevant bits of my spec_helper file are:
require 'rubygems'
require 'factory_girl'
require 'support/controller_macros'
require 'devise'
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.include ControllerMacros, :type => :controller
end
And my spec file is:
require 'rails_helper'
RSpec.describe UsersController, :type => :controller do
describe "GET show" do
login_admin
it "returns http success" do
user = User.first
get :show, id: user[:id]
expect(response).to be_success
end
end
end
Make sure that rails_helper.rb loads files in spec/support folder.
If not add that line to your rails_helper.rb:
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }`
after require 'rspec/rails' and move devise config to rails_helper.rb
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
config.include ControllerMacros, :type => :controller
end

Rails 4 Devise + Omniauth Github Routing Error

I'm using Rails 4.1.1 and I'm trying to create omniauth github with my devise gem in my application. But I get routing error, I'm following Railscast #235 revised on this topic.
Right now, I'm trying to do the same thing Ryan Bates is doing, which is raise omniauth.auth as a yaml to the screen, but I get the error:
No route matches [GET] "/auth/github/callback"
How do I fix this error?
Here you have my routes:
Rails.application.routes.draw do
devise_for :users, controllers: {omniauth_callbacks: "omniauth_callbacks"}
root to: 'questions#index'
resources :questions do
resources :answers, only: [:create]
end
resources :users, only: [:show]
#USERS CONTROLLER MY ROUTES
get "adding_likes/(:id)/(:like)/(:current_user_id)", to: "answers#adding_likes", as: :adding_likes
get "add_accept/(:answer_id)", to: "answers#accept", as: :accept
get "leaderboard", to: "users#leaderboard", as: :leaderboard
end
My controller:
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def all
raise request.env["omniauth.auth"].to_yaml
end
alias_method :github, :all
end
My user model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :omniauthable
# :recoverable, :rememberable and :trackable
devise :database_authenticatable, :registerable, :validatable, :omniauthable
has_attached_file :avatar, :styles => { :small => "100x100>" }
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
# has_attached_file :superstarbadge, :styles => { :small => "100x100>" }
# validates_attachment_content_type :superstarbadge, :content_type => /\Aimage\/.*\Z/
has_many :questions
has_many :answers
def to_s
email
end
end
Important part of my devise.rb
config.omniauth :github,NOT-VISIBILE,IN-HERE
Basically, you haven't added the routes for it. I think you've missed Ryan add it to his routes file. Just add this:
get "/auth/github/callback" => "yourController#yourAction"
but since you're using omniauth, it's better this way
get "/auth/:provider/callback" => "yourController#yourAction
And add the necessary views for it.

devise current_user found in a session controller, but it became nil when comes to another controller

I am using devise for authentication,
I put some text after "super" in SessionsController#create method like below, to find the "current_user" value in the browser(using better_errors gem).
class SessionsController < Devise::SessionsController
def new
super
end
def create
super
error_debug
end
def edit
super
end
end
in routes.rb
root :to => "welcome#index"
I got the current_user value like expected in SessionsController#create, but current user value is 'nil' when it comes to welcome#index method even after signin.
I can't able to find the reason behind it, please someone assist me to resolve this issue.
First signed in and check in better_error value of current_user
I checked the log file, I found that, the bug is
Dall::error server not found
Then I installed memcached by doing sudo apt-get install memcached. It works fine now. For more clarification, click the below link
Rails + Dalli memcache gem: DalliError: No server available
Follow the below steps:
gem install 'devise'
rails generate devise:install
rails generate devise User
rake db:migrate
Your Routes should be like this
devise_for :users
>
root :to => "home#index"
Change your development config add following line to config/environments/development.rb
config.action_mailer.default_url_options = { host: 'localhost:3000' }
Add this code to your controller
before_filter :authenticate_user!
And if you are using rails 4 so add this code to your application
class ApplicationController < ActionController::Base
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit({ roles: [] }, :email, :password, :password_confirmation) }
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:username, :email) }
end
end
Do you have protect_from_forgery method call in your application controller? Look at this guides.rubyonrails.org/security.html