Could not find devise mapping for controller path - ruby-on-rails

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

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

In Rails 4 How to Test Devise Custom Registration Paths?

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.

Rails 4 + Rails-API + RSpec + Devise - Trouble testing controllers with custom devise routes

I'm currently building out an API and trying to test my controller. I am getting errors related to Devise that I cannot solve. Here is what I currently have:
# /spec/requests/api/v1/sessions_spec.rb
require 'spec_helper'
describe API::V1::SessionsController do
describe "POST /sessions" do
it "on success, returns the user's authentication token" do
user = FactoryGirl.create(:user)
user_params = {
"user" => {
"email" => user.email,
"password" => user.password
}
}.to_json
post "/api/v1/sessions", user_params
expect(response.status).to eq(200)
json = JSON.parse(response.body)
expect(json['authentication_token']).to eq(user.authentication_token)
end
end
end
# /app/controllers/api/v1/sessions_controller.rb
class API::V1::SessionsController < Devise::SessionsController
respond_to :json
def create
end
end
# routes.rb
MyApplication::Application.routes.draw do
namespace :api, defaults: { format: :json } do
namespace :v1 do
devise_scope :user do
post 'sessions' => 'sessions#create', :as => 'login'
end
end
end
end
The error am I current getting is:
1) API::V1::SessionsController POST /sessions on success, returns the user's authentication token
Failure/Error: post "/api/v1/sessions", user_params
AbstractController::ActionNotFound:
Could not find devise mapping for path "/api/v1/sessions".
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]
# ./spec/requests/api/v1/sessions_spec.rb:21:in `block (3 levels) in <top (required)>'
I have searched around for a solution to this issue, and tried a bunch from different questions. The question's solution here (How to write controller tests when you override devise registration controller?) said to put the below code in a before block in the spec.
before :each do
request.env['devise.mapping'] = Devise.mappings[:user]
end
But when I add that to my controller, I get another error that says:
1) API::V1::SessionsController POST /sessions on success, returns the user's authentication token
Failure/Error: request.env['devise.mapping'] = Devise.mappings[:user]
NoMethodError:
undefined method `env' for nil:NilClass
# ./spec/requests/api/v1/sessions_spec.rb:7:in `block (2 levels) in <top (required)>'
Another question's solution here (undefined method 'env' for nil:NilClass) says to try adding include Devise::TestHelpers to the spec but when I do that, the same error is returned.
The current gems I am using:
rails (4.0.2)
rails-api (0.2.0)
rspec-rails (2.14.1)
devise (3.2.2)
Using these two tutorials as sort of a guide: https://lucatironi.github.io/tutorial/2012/10/15/ruby_rails_android_app_authentication_devise_tutorial_part_one/
http://commandercoriander.net/blog/2014/01/04/test-driving-a-json-api-in-rails/
Any help would be appreciated, thanks!
I think I may have solved the issue. It seems I had to add in a devise_for outside the api namespace blocks:
MyApplication::Application.routes.draw do
devise_for :users, skip: [:sessions, :passwords, :registrations]
namespace :api, defaults: { format: :json } do
namespace :v1 do
devise_scope :user do
post 'sessions' => 'sessions#create', :as => 'login'
end
end
end
end
I will keep the question open for a bit longer in case I run into any other issues when testing.
Replace
request.env['devise.mapping'] = Devise.mappings[:user]
With
#request.env['devise.mapping'] = Devise.mappings[:user]
Notice the error message specifically suggests to use:
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]

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

Testing an RSpec controller action that can't be accessed directly

I've got a controller that can't be accessed directly, in the traditional RESTful way, but rather only through a particular url.
Normally I'm used to using get and post in my controller specs to call controller actions. Is there a way that I can exercise my controller by visiting a particular url?
EDIT:
Here is my route:
Larzworld::Application.routes.draw do
match '/auth/:provider/callback' => 'authentications#create'
devise_for :users, :controllers => {:registrations => "registrations"}
root :to => 'pages#home'
end
Here is my spec:
require 'spec_helper'
describe AuthenticationsController do
before(:each) do
request.env["omniauth.auth"] = {"provider" => "twitter", "uid" => "12345678"}
end
describe 'POST create' do
it "should find the Authentication using the uid and provider from omniauth" do
Authentication.should_receive(:find_by_provider_and_uid)
post 'auth/twitter/callback'
end
end
end
and here is the error I receive:
Failures:
1) AuthenticationsController POST create should find the Authentication using the uid and provider from omniauth
Failure/Error: post 'auth/twitter/callback'
No route matches {:action=>"auth/twitter/callback", :controller=>"authentications"}
# ./spec/controllers/authentications_controller_spec.rb:13
Finished in 0.04878 seconds
1 example, 1 failure
Controller tests use the four HTTP verbs (GET, POST, PUT, DELETE), regardless of whether your controller is RESTful. So if you have a non-RESTful route (Rails3):
match 'example' => 'story#example'
the these two tests:
require 'spec_helper'
describe StoryController do
describe "GET 'example'" do
it "should be successful" do
get :example
response.should be_success
end
end
describe "POST 'example'" do
it "should be successful" do
post :example
response.should be_success
end
end
end
will both pass, since the route accepts any verb.
EDIT
I think you're mixing up controller tests and route tests. In the controller test you want to check that the logic for the action works correctly. In the route test you check that the URL goes to the right controller/action, and that the params hash is generated correctly.
So to test your controller action, simply do:
post :create, :provider => "twitter"`
To test the route, use params_from (for Rspec 1) or route_to (for Rspec 2):
describe "routing" do
it "routes /auth/:provider/callback" do
{ :post => "/auth/twitter/callback" }.should route_to(
:controller => "authentications",
:action => "create",
:provider => "twitter")
end
end

Resources