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
Related
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.
I've got a Rails engine (for authentication, though I don't think it will matter). It comes, as is traditional, with a dummy app in the /spec directory.
The engine defines a couple of routes in the application, and the dummy app defines a few of its own.
When I run the dummy app in WEBrick using rails s, it works fine. However, when try to run it using Pow (symlinking to my_engine/spec/dummy in ~/.pow), it doesn't seem to recognize the routes provided by the engine, and I get:
ActionController::UrlGenerationError at /login
No route matches {:action=>"new", :controller=>"my_engine/sessions"}
What's wrong with this, and why does it only happen in Pow?
Here are the routes files, in case it matters:
my_engine/config/routes.rb:
Rails.application.routes.draw do
get 'login', :controller => 'my_engine/sessions', :action => :new
post 'login', :controller => 'my_engine/sessions', :action => :create
get 'logout', :controller => 'my_engine/sessions', :action => :destroy
end
my_engine/spec/dummy/config/routes.rb:
Rails.application.routes.draw do
mount MyEngine::Engine => "/auth"
root 'home#index'
controller :home do
get :index
get :private, :as => 'private'
end
end
Edit: I've determined that this doesn't work when the engine is used in a normal application as well, so it's not just the dummy app.
So, turned out to be completely unrelated. The engine-served page I was linking to contained a link_to helper in which the second argument (the path) was dependent on ENV['RAILS_ENV']. In Pow, this value was nil (whereas in WEBrick, it was set to 'development', as the author presumably expected), and so it was trying to render link_to('text', nil). Which somehow produced the above error.
I have an rails application where i used slugged urls. How can i test those urls with Rspec.
rake routes is generating following results
new_user_session GET /login(.:format) sessions#new
user_session POST /login(.:format) sessions#create
bays GET /:slug/bays(.:format) bays#index
POST /:slug/bays(.:format) bays#create
new_bay GET /:slug/bays/new(.:format) bays#new
edit_bay GET /:slug/bays/:id/edit(.:format) bays#edit
bay GET /:slug/bays/:id(.:format) bays#show
PATCH /:slug/bays/:id(.:format) bays#update
PUT /:slug/bays/:id(.:format) bays#update
DELETE /:slug/bays/:id(.:format) bays#destroy
Now when i run rpec for bays controller, i caught with following error.
Failure/Error: get :index, {}, valid_session
ActionController::UrlGenerationError:
No route matches {:action=>"index", :controller=>"bays"}
Using
rspec-rails (3.0.1)
rails (4.0.0)
As you have defined it... bays index requires a :slug parameter. According to the error message, you have passed no slug to this route. You must either pass :slug (eg get :index, :slug => "1234", valid_session) or redefine the route to not need it.
for testing routes in rspec... That's a basic part of rspec: rspec routing specs
in your case it'd be something like:
{ :get => "/1234/bays" }.
should route_to(
:controller => "bays",
:slug => "1234"
)
For rspec 3.x you can go with:
scenario 'get correct slug', :type => :routing do
expect(get("/models/slug")).to route_to(:controller => "controller_name", :action => 'action_name', :id => "your_slug_name")
end
Note the type: :routing definition before the block.
More info here.
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.
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