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.)
Related
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.
After I upgraded my app from rails 4 to rails 5 and rspec-rails from 3.0 to 3.6 when running specs after upgrade I get errors:
No route matches {:action=>"/login", :controller=>"posts", :session=>{:email=>"email#example.com", :password=>"password"}}
My route is defined like this:
post 'login' => 'sessions#create'
I'm trying to access it from my helper like this:
def log_in_as(user, options = {})
post login_path , params:{session: { email: user.email,
password: "password"}}
end
In my controller spec:
RSpec.describe PostsController, type: :controller, posts: true
do
before(:each) { setup }
def setup
#user = FactoryGirl.create(:user, email: 'email#example.com')
log_in_as #user
end
#tests Code...
end
Edit:
rake routes | grep login Output:
sessions#new login GET /login(.:format)
sessions#create POST /login(.:format)
I'm new to rails, and I'm trying to build API following Code School tutorial.
I get this error while trying to post to '/users' path.
routes file code :
Rails.application.routes.draw do
namespace :api,constraints: {subdomain: 'api'}, path: '/' do
resources :users, except: :delete
end
end
and the test code is :
require 'test_helper'
class CreatingUsersTest < ActionDispatch::IntegrationTest
test 'create users' do
post api_users_path,
{user: {name: 'test', email:'test#test.com'}}.to_json,
{'Accept' => Mime::JSON, 'Content-Type': Mime::JSON.to_s}
assert_equal response.status, 201
assert_equal response.content_type, Mime::JSON
user = json(response.body)
assert_equal api_user_url(user[:id]), response.location
end
end
And when I use rake routes :
api_users GET /users(.:format) api/users#index {:subdomain=>"api"}
POST /users(.:format) api/users#create {:subdomain=>"api"}
....
In the route you constraint the subdomain to be api
namespace :api,constraints: {subdomain: 'api'}, path: '/' do
but then in the test you call api_user_path
post api_users_path
that uses a generic hostname (test.host), and not the api hostname. The solution is to pass a specific host to the helper that satisfies the requirement.
post api_users_path(host: "api.test.host")
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
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!