I have the following routes setup inside a rails api app:
constraints subdomain: 'api', path: '/' do
namespace 'api', path: '/' do
scope module: 'v1' , constraints: ApiConstraints.new(version: 1, default: true) do
devise_scope :user do
post 'sign_in' => 'sessions#create'
delete 'sign_out' => 'sessions#destroy'
end
resources :question
end
end
end
# rake routes
Prefix Verb URI Pattern Controller#Action
api_sign_in POST /sign_in(.:format) api/v1/sessions#create {:subdomain=>"api"}
api_sign_out DELETE /sign_out(.:format) api/v1/sessions#destroy {:subdomain=>"api"}
When I got to execute this test:
require 'rails_helper'
RSpec.describe Api::V1::SessionsController, :type => :controller do
describe '#create' do
it 'creates a session' do
#request.env["devise.mapping"] = Devise.mappings[:user]
user = User.create(email: 'rob#edukate.com', password: 'password')
post :create, action: :create, user_login: { password: user.password, email: user.email}
expect(response).to be_success
end
end
end
I get this error:
F
Failures:
1) Api::V1::SessionsController#create creates a session
Failure/Error: post :create, action: :create, user_login: { password: user.password, email: user.email}
AbstractController::ActionNotFound:
Could not find devise mapping for path "/sign_in?user_login%5Bemail%5D=rob%40edukate.com&user_login%5Bpassword%5D=password".
I have tried many variations of routes but I cannot get this simple test to pass. If I cannot get it completed like this I'll just roll my own Auth; but it appears devise is doing something odd with a Abstract controller. Thanks in advance!
Related
I am trying to test patch for devise user info using Rspec, the update url looks like this # PATCH/PUT /api/users/1 but I am getting this error for all the following cases
Error ArgumentError:
wrong number of arguments (given 2, expected 1)
Cases I tried
patch :update, {'id'=> #api_user['user']['id'], 'user' => attributes_for(:normal_user)}
patch :update, 'id'=> #api_user['user']['id'], 'user' => attributes_for(:normal_user)
patch :update, 'id'=> #api_user['user']['id'], :params => {'user' => attributes_for(:normal_user)}
And I tried this
patch :update, :params => {'user' => create(:normal_user)}. # this one has the id within
but gives this error
No route matches {:action=>"update", :controller=>"api/users", :user=>#<User id: 227794695, email: "test11#example.com", created_at: "2020-05-03 08:51:55", updated_at: "2020-05-03 08:51:55", is_admin: nil, first_name: "test", last_name: "test">} which make sence, the url should be update/id
you should not put "update" after patch, since
patch it self will automatically will route to update to user controller
this is the reason error message informed you given 2, expected 1 arguments
here is sample for your reference from update
require 'rails_helper'
RSpec.describe 'User request', type: :request do
it 'should update user email' do
patch "/api/users/#{#api_user['user']['id']}",
params: {
user: {
email: 'new_email_address#gmail.com'
}
},
as: :json
expect(response).to have_http_status(:success)
end
end
here is sample of /config/routes.rb for your reference to update users
Rails.application.routes.draw do
namespace :api, defaults: { format: :json } do
resources :users, only: [:create, :update, :destroy]
end
end
I'm getting two different test errors in Chapter 8.2.1 of Michael Hartl's Ruby on Rails Tutorial. I'm new to Rails, but I have triple checked for syntax errors and anything else I could find. Any help is very much appreciated.
Error Message 1:
ERROR["test_should_get_new", Minitest::Result, 0.9693643249993329]
test_should_get_new#Minitest::Result (0.97s)
NameError: NameError: undefined local variable or method users_new_url' for #<UsersControllerTest:0x00000006e953f8>
test/controllers/users_controller_test.rb:5:inblock in '
Error Message 2:
ERROR["test_invalid_signup_information", Minitest::Result, 0.8977870759990765]
test_invalid_signup_information#Minitest::Result (0.90s)
ActionController::RoutingError: ActionController::RoutingError: No route matches [POST] "/signup"
test/integration/users_signup_test.rb:8:in block (2 levels) in <class:UsersSignupTest>'
test/integration/users_signup_test.rb:7:inblock in '
Routes.rb
Rails.application.routes.draw do
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users
end
Sessions Controller:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
# Log the user in and redirect to the user's show page.
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
end
end
Users Controller:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:success] = "Welcome to your new profile!"
redirect_to #user
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
Users controller test
require 'test_helper'
class UsersControllerTest < ActionDispatch::IntegrationTest
test "should get new" do
get users_new_url
assert_response :success
end
end
Users Signup Test
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user#invalid",
password: "foo",
password_confirmation: "bar" } }
end
assert_template 'users/new'
assert_select 'div#error_explanation'
assert_select 'div.field_with_errors'
assert_select 'form[action="/signup"]'
end
test "valid signup information" do
get signup_path
assert_difference 'User.count', 1 do
post users_path, params: { user: { name: "Example User",
email: "user#example.com",
password: "password",
password_confirmation: "password" } }
end
follow_redirect!
assert_template 'users/show'
assert_not flash.nil?
end
end
In your users_controller_test, the code doesn't know what users_new_url is. This is probably because that route doesn't exist. You will most likely have to do something like get new_user_path, but you can find out by typing rake routes and getting the list of your available routes.
Here's an example of what rake routes will output:
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
You can reference a named path by appending _path to the name. i.e. users_path will map to the "users#index"controller and method.
rake routes will also help for your second problem, which is that you do not have a route defined for POST /signup -- you have a GET /signup.
So, you'll need to add a line like:
post '/signup', to: 'users#create'
This route will map to your UsersController's #create method, which I see you have in your code.
Rails 5.1
RSpec 3.6
I have a Controller:
class SessionController < ApplicationController
def new
end
end
A custom route:
get 'login' => 'sessions#new'
RSpec Test:
require 'rails_helper'
RSpec.describe SessionController, type: :controller do
describe "GET #new" do
before do
routes.draw { get "login" => "sessions#new" }
end
it "returns http success" do
get :login
expect(response).to have_http_status(:success)
end
end
end
and get error:
ActionController::UrlGenerationError: No route matches {:action=>"login", :controller=>"session"}
So "get" within a controller test seems always map to the action not the route. What should i do to get this test run? thanks in advance.
ActionController::UrlGenerationError: No route matches
{:action=>"login", :controller=>"session"}
Your controller name is SessionController, so your route should be
get 'login' => 'session#new' not get 'login' => 'sessions#new'
require 'rails_helper'
RSpec.describe SessionController, type: :controller do
describe "GET #new" do
before do
routes.draw { get "login" => "session#new" }
end
it "returns http success" do
get :login
expect(response).to have_http_status(:success)
end
end
end
Change it in your routes.rb as well.
When you are writing tests and you use the methods get, post, delete, etc., those methods assume that any parameter you pass them is the name of an action within the controller being tested. So, this works:
get :new
because it generates url_for(:controller => :sessions, :action => :new).
This doesn't work:
get '/login'
because it generates url_for(:controller => :sessions, :action => '/login').
I ran rails g devise:controllers users and my routes look like
devise_for :users, controllers: {
sessions: 'sessions'
}
I have a app/controllers/users/sessions_controller that I want to test. My routes look like:
$ rake routes
Prefix Verb URI Pattern Controller#Action
new_user_session GET /users/sign_in(.:format) sessions#new
user_session POST /users/sign_in(.:format) sessions#create
I set up my test like this:
# test/controllers/users/sessions_controller_test.rb
require 'test_helper'
class SessionsControllerTest < ActionController::TestCase
tests Users::SessionsController
test "sign in sanity check" do
user = users(:one)
post :create, email: user.email, password: 'password'
assert_response :success
end
end
Which fails with the error:
ActionController::UrlGenerationError: No route matches {:action=>"create", :controller=>"users/sessions", :email=>"one#example.com", :password=>"password"}
How do I correctly POST to the URL I want? I've tried all sorts of variations, but I'm missing some magic sauce here.
Change your routes.rb to
devise_for :users, controllers: {
sessions: 'users/sessions'
}
And in your test, make sure you have
class SessionsControllerTest < ActionController::TestCase
tests Users::SessionsController
include Devise::TestHelpers
test "sign in sanity check" do
request.env["devise.mapping"] = Devise.mappings[:user]
user = users(:one)
post :create, email: user.email, password: 'password'
assert_response :success
end
end
(You can see how Devise itself does it for an example.)
I am having problems trying to post as JSON to my controller action within my rspec test
RSpec.describe RegistrationsController, type: :controller do
context 'Adding a Valid User' do
it 'Returns Success Code and User object' do
#params = { :user => {username: 'name', school: 'school'} }.to_json
post :create, #params
end
end
end
At the moment I want to get a successful post request firing but am getting this error back all the time
Failure/Error: post :create, #params
AbstractController::ActionNotFound:
Could not find devise mapping for path "/lnf".
My routes are setup like so
Rails.application.routes.draw do
constraints(subdomain: 'api') do
devise_for :users, path: 'lnf', controllers: { registrations: "registrations" }
devise_scope :user do
post "/lnf" => 'registrations#create'
end
end
end
Rake routes outputs the following
Prefix Verb URI Pattern Controller#Action
user_registration POST /lnf(.:format) registrations#create {:subdomain=>"api"}
lnf POST /lnf(.:format) registrations#create {:subdomain=>"api"}
So i have 2 declarations for the same action?
Could anyone shed some light on this please
Thanks
Ok so after a painstaking few hours i have got my tests passing correctly and posting as json by doing the following
require 'rails_helper'
RSpec.describe RegistrationsController, type: :controller do
before :each do
request.env['devise.mapping'] = Devise.mappings[:user]
end
context 'Adding a Valid User' do
it 'Returns Success Code and User object' do
json = { user: { username: "richlewis14", school: "Baden Powell", email: "richlewis14#gmail.com", password: "Password1", password_confirmation: "Password1"}}.to_json
post :create, json
expect(response.code).to eq('201')
end
end
end
My Routes are back to normal
Rails.application.routes.draw do
constraints(subdomain: 'api') do
devise_for :users, path: 'lnf', controllers: { registrations: "registrations" }
end
end
And in my test environment I had to add
config.action_mailer.default_url_options = { host: 'localhost' }
The key here was
request.env['devise.mapping'] = Devise.mappings[:user]
Not fully sure as yet to what it does, its next on my list to find out, but my tests are starting to run and pass