In Rails 4 How to Test Devise Custom Registration Paths? - ruby-on-rails

I have customized the devise paths like this :
devise_for :users, path: '', path_names: { sign_in: 'signin', sign_out: 'signout', password: 'secret', confirmation: 'verification', unlock: 'unblock', registration: 'signup', sign_up: 'new' }
So for example it makes the user signup path like this http://localhost:3000/signup/new. Now the problem is, I don't know how to test this page. Here's the test for users_controller :
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
test "should get signup" do
get :signup_new
assert_response :success
assert_select "title", "SignUp | #{Figaro.env.app_name}"
end
end
How to visit these custom paths in my test files?

You are probably testing the wrong controller. Check your routes (rake routes) to see which controller is assigned to handle your Devise authentication routes. By default I believe it is Devise::SessionsController.
I do not think this is your intent, but it is worth mentioning that you should not test the devise gem.
Note that requests for ActionController::TestCase are simulated. If you want to test true requests then you should look into integration testing. A brief overview can be found here
UPDATE
Take a look in the Devise gem for some examples of how to log a user in here. Notice that include Devise::TestHelpers is added near the top of the test class which adds a sign_in method.

Related

ActionController::UrlGenerationError for devise registration controller

I'm running into an interesting ActionController::UrlGenerationError while testing my custom devise User::RegistrationsController
Here's the error:
ActionController::UrlGenerationError:
No route matches {:action=>"create", :controller=>"user/registrations", :user=>{:name=>"Adriane Koepp", :city=>"Nidiafurt", :address=>"14955 Cormier Viaduct", :country=>"Mozambique", :email=>"moshe#kozey.com", :phone_primary=>"(295) 491-0447 x9108", :phone_secondary=>"536.985.9499 x7264", :postal_code=>"93438-7448", :province=>"South Carolina", :password=>"MaH9R5G8XqB", :pets_attributes=>[{:name=>"Patches", :chip_number=>"149793073311890", :species=>"iusto"}]}}
The error shows up in all my tests for this controller, but for simplicity, I'll only list one test for the create action:
it 'creates a new User' do
expect do
post :create, params: { user: valid_attributes }
end.to change(User, :count).by(1)
end
The routes.rb contains:
devise_for :users, controllers: {
registration: 'user/registration'
}
I can navigate to my registration page just fine at http://localhost:3000/users/sign_up, but for some reason, my tests don't seem to think this controller has a valid URL for the create action. Why so?
Additional steps
Upon following D1ceWard's suggestion, I pluralized "registration" in my routes, and now the error message changed to a AbstractController::ActionNotFound error. I countered that by following the documentation and added the following block to the top of my tests:
before(:each) do
#request.env['devise.mapping'] = Devise.mappings[:user]
end
Your error is caused by missing pluralization, devise don't know what registration is, but work with registrations.
Solution :
# routes.rb
devise_for :users, controllers: {
registrations: 'user/registrations'
}
You can use rails routes to check all existings routes.
Devise doc about custom controllers

Could not find devise mapping for controller path

I know there are a ton of questions with this same problem but none of the solutions seem to be working for my situation.
I've defined my own sessions, registrations, and users controllers since I'm using an API with token authentication, but I can't seem to get my routes/scoping working.
I get the following error message upon trying to run an Spec test on my sessions controller (which inherits from DeviseController)
Failure/Error: post :create, credentials
AbstractController::ActionNotFound:
Could not find devise mapping for path "/api/v1/sessions/create?user_login%5Bemail%5D=rosina%40russel.name&user_login%5Bpassword%5D=12345678".
This may happen for two reasons:
1) You forgot to wrap your route inside the scope block. For example:
devise_scope :user do
get "/some/route" => "some_devise_controller"
end
2) You are testing a Devise controller bypassing the router.
If so, you can explicitly tell Devise which mapping to use:
#request.env["devise.mapping"] = Devise.mappings[:user]
I've actually done both of these things based on other answers that I've read, but I don't think its done correctly for my specific situation.
Here is the spec test I'm trying to run
describe "POST #create" do
before(:each) do
#user = FactoryGirl.create :user
#user.skip_confirmation!
#request.env['devise.mapping'] = Devise.mappings[:user]
end
context "when the credentials are correct" do
before(:each) do
credentials = { user_login: { :email => #user.email, :password => "12345678"} }
post :create, credentials
end
it "returns the user record corresponding to the given credentials" do
#user.reload
user_response = JSON.parse(response.body, symbolize_names: true)
expect(user_response[:auth_token]).to eql #user.auth_token
end
it { should respond_with :created }
end
end
As you can see, I'm specifying what it's saying that I haven't specified.
Here is the snippet from my routes.rb before the definition of my API in it's namespace:
namespace :api, defaults: { format: :json } do
namespace :v1 do
devise_for :users, skip: [:sessions, :registrations]
devise_scope :user do
post 'sessions', to: 'sessions#create'
delete 'sessions', to: 'sessions#destroy'
end
If anyone sees anything from with this please let me know, I'd love to test my controller...
And yes, my sessions_controller is located in app/controllers/api/v1/sessions_controller.rb and it is properly defined as class API::V1::SessionsController < DeviseController
I noticed that devise_for is supposed to SET Devise.mapping but that seems to not be happening at all!
I should've done this right away. I sent Devise.mappings.inspect to the logger and it read that the mapping was stored in :api_v1_user so the key in the mapping hash corresponds to the namespace that you wrote devise_for in... hopefully this will help others
Based on your routes, API is in json format. So you need to specify that when sending post method. Which is why I think rspec is complaining about that route not existing because it doesn't know to use json. Try changing this
post :create, credentials
to this
post :create, credentials, format: :json

how to login in with devise using rspec

This is my rspec file
it "assigns all tool_cvt_remote_focus as #tool_cvt_remote_focus" do
post "/users/sign_in", {:username => 'user', :password => "test"}
end
rake routes
users/sessions#new {:locale=>"jp"} new_user_session_en GET /users/sign_in(.:format)
The error on console with POST method
Failure/Error: post "/users/sign_in", {:user => 123}
ActionController::UrlGenerationError:
No route matches {:action=>"/users/sign_in", :controller=>"users", :user=>"123"}
The error with GET method
Failure/Error: get "/users/sign_in", {:username => 'user', :password => "test"}
Okay, I did some research and with Devise and Rspec and got your test working, albeit with some refactoring.
What I had to do was use the Devise helpers, to do that I had to add that capability into my spec_helper file:
require 'devise'
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
Then I had to refactor your test to use these new Devise helper methods, otherwise Devise would throw me an error about trying to skip over the router.
require 'rails_helper'
RSpec.describe Devise::SessionsController, :type => :controller do
it "assigns all tool_cvt_remote_focus as #tool_cvt_remote_focus" do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = User.create(
username: 'user',
password: 'test'
)
sign_in :user, user
end
end
Now, the way I created the user is not a best practice. From what I could tell online you are supposed to use FactoryGirl to to create the user because that is less fragile. However, as I have never used FactoryGirl and just wanted to give you the simplest solution possible, I decided not to include it.

functional test to confirm custom route with devise

I have a custom route for devise:
devise_scope :user do
get '/login' => "devise/sessions#new", :as => :new_user_session
get '/logout' => 'devise/sessions#destroy',
...
I want to make sure that when the request /login is called, that it gets correctly routed to devise and that the response is successful.
How do you test for custom routing and a successful request?
If you're using the built-in TestUnit to test, check out the Rails Guide on testing controllers.
If you're using Rspec, check out the Rspec Github Page for information on testing controllers.
It depends on you testing suite, personally I use RSpec + Capybara for my Rails projects.
If you don't have a testing suite yet I highly recommend this guide.
http://everydayrails.com/2012/03/12/testing-series-intro.html
In this particular instance, I'd say you want two tests. One for the routing and one for a successful request. Using rspec, this will look something like:
#spec/routing/devise_routing_spec.rb
require 'spec_helper'
describe "Devise Routes" do
it "should route the login correctly" do
{:get => "login"}.should route_to(controller: "devise/sessions", action: "new")
end
end
and
#spec/controllers/devise/session_controller_spec.rb
require 'spec_helper'
describe Devise::Sessions do
it "should be successful with a login request" do
get "new"
response.should be_success
end
end

how to TDD deleting a user in devise

I setup Devise so I can write controller specs with this.
Then I setup Devise so users cannot delete their accounts.
Now I want to write a spec to make sure the controller is unable to call the destroy action on the Devise user. How do I write this?
In my controller the Devise part looks like this
devise_for :users, skip: :registrations do
resource :registration,
only: [:new, :create, :edit, :update],
path: 'users',
path_names: { new: 'sign_up' },
controller: 'devise/registrations',
as: :user_registration do
get :cancel
end
end
In my spec I'm trying to do the following but it doesn't work. I'm not even sure I'm writing it right. I think the page I'm trying to access is wrong.
describe UsersController do
login_user # from devise controller helper
it "does not allow deleting of user" do
get :users, :method => :delete
# assert here user was not deleted
end
end
I think what you really want to test is whether or not the route exists for the registrations#destroy action. If there is no route, then the action will not be called since it can't be routed to the controller.
For a destroy action, we need to try to route a DELETE action to the users path. so, something like this might do the trick:
{ :delete=> "/users" }.should_not be_routable
Test syntax pulled from a similar answer here:
Rails RSpec Routing: Testing actions in :except do NOT route
Your mixing your http verbs for one thing. You should be doing
delete :destroy, id: #user
Your going to have to get #user from somewhere, I have it set by controller macros personally.
Then you can either check the response header for unsuccessful, or more easily
#user.should exist
I would put the following in my controller spec when testing this kind of thing (although i'd use FactoryGirl to create my test user):
it "does not allow deletion of a user" do
user = User.create!([insert valid args here])
expect {
delete :destroy, id: user
}.not_to change(User, :count)
end

Resources