Rspec fails with ActionController::UrlGenerationError with a URL I would think is valid. I've tried messing with the params of Rspec request, as well as fiddled with the routes.rb, but I'm still missing something.
The weird thing is, it works 100% as expected when testing locally with curl.
Error:
Failure/Error: get :index, {username: #user.username}
ActionController::UrlGenerationError:
No route matches {:action=>"index", :controller=>"api/v1/users/devices", :username=>"isac_mayer"}
Relevant code:
spec/api/v1/users/devices_controller_spec.rb
require 'rails_helper'
RSpec.describe Api::V1::Users::DevicesController, type: :controller do
before do
#user = FactoryGirl::create :user
#device = FactoryGirl::create :device
#user.devices << #device
#user.save!
end
describe "GET" do
it "should GET a list of devices of a specific user" do
get :index, {username: #user.username} # <= Fails here, regardless of params. (Using FriendlyId by the way)
# expect..
end
end
end
app/controllers/api/v1/users/devices_controller.rb
class Api::V1::Users::DevicesController < Api::ApiController
respond_to :json
before_action :authenticate, :check_user_approved_developer
def index
respond_with #user.devices.select(:id, :name)
end
end
config/routes.rb
namespace :api, path: '', constraints: {subdomain: 'api'}, defaults: {format: 'json'} do
namespace :v1 do
resources :checkins, only: [:create]
resources :users do
resources :approvals, only: [:create], module: :users
resources :devices, only: [:index, :show], module: :users
end
end
end
Relevant line from rake routes
api_v1_user_devices GET /v1/users/:user_id/devices(.:format) api/v1/users/devices#index {:format=>"json", :subdomain=>"api"}
The index action requires a :user_id parameter, but you haven't supplied one in the params hash. Try:
get :index, user_id: #user.id
The error message is a bit confusing, because you aren't actually supplying a URL; instead you are calling the #get method on the test controller, and passing it a list of arguments, the first one is the action (:index), and the second is the params hash.
Controller specs are unit tests for controller actions, and they expect that the request parameters are correctly specified. Routing is not the responsibility of the controller; if you want to verify that a particular URL is routed to the right controller action (since as you mention, you are using friendly-id), you may want to consider a routing spec.
Related
I keep getting this error message:
NameError - uninitialized constant Api::SessionsController:
But I've double checked and my routes configuration looks correct:
Rails.application.routes.draw do
namespace :api, defaults: {format: :json} do
resources :users, only: :create
resource :session, only: [:create, :destroy]
end
root 'static_pages#root'
end
My controller is also using the singular session:
class Api::SessionController < ApplicationController
def create
#user = User.find_by_credentials(
params[:user][:username],
params[:user][:password]
)
if #user
log_in(#user)
render 'api/users/show'
else
render json: ['Your request failed. Please try again.'], status: 401
end
end
And my folder structure is as follows:
Rails have very good official guide.
Session is a singular resource without referencing an ID.
Because you might want to use the same controller for a singular route (/account) and a plural route (/accounts/45), singular resources map to plural controllers. So that, for example, resource :photo and resources :photos creates both singular and plural routes that map to the same controller (PhotosController).
So you need to rename your controller to Api::SessionsController.
if wanna keep as you have set it up.
post 'session' => 'session#create', as: :session_create
destroy 'session' => 'session#destroy', as: :session_destroy
I have defined a singular resource in my routes.rb which looks like this
Rails.application.routes.draw do
resource :dog, only: [:create], to: "dog#create", controller: "dog"
end
After that I've defined a controller with a create action like this
class DogController < ApplicationController
def create
render json: {}, status: :ok
end
end
And now I'm trying to test it out with RSpec like this
require "rails_helper"
describe DogController do
it "works" do
post :create, params: { foo: :bar }
end
end
This is throwing this error instead of passing:
ActionController::UrlGenerationError:
No route matches {:action=>"create", :controller=>"dog", :foo=>:bar}
What am I doing wrong?
Change your route to
resource :dog, only: [:create], :controller => "dog"
It is better to use plural controllers even if its a singular resource
http://edgeguides.rubyonrails.org/routing.html#singular-resources
Your create action is not taking in any parameter. It's just rendering json and returning a status code
I'm getting this error.When I want to run te server on localhost:3000/api/v1/songs.json
Routing Error
uninitialized constant API::V1::SongsController.
Thats my routes.rb file:
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :songs, only: [:index, :create, :update, :destroy]
end
end # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
Routes
Routes match in priority from top to bottom
Helper HTTP Verb Path Controller#Action
Path / Url
Path Match
api_v1_songs_path GET /api/v1/songs(.:format)
api/v1/songs#index
POST /api/v1/songs(.:format)
api/v1/songs#create
api_v1_song_path PATCH /api/v1/songs/:id(.:format)
api/v1/songs#update
PUT /api/v1/songs/:id(.:format)
api/v1/songs#update
DELETE /api/v1/songs/:id(.:format)
api/v1/songs#destroy
and thats my SongsController:
class Api::V1::SongsController < Api::V1::BaseController
def index
respond_with Song.all
end
def create
respond_with :api, :v1, Song.create(song_params)
end
def destroy
respond_with Song.destroy(params[:id])
end
def update
song = Song.find(params["id"])
song.update_attributes(song_params)
respond_with song, json: song
end
private
def song_params
params.require(:song).permit(:id, :name, :singer_name, :genre, :updated_at, :tag)
end
end
I'm going to copy how we have this running, cause I cannot see a direct difference between your code and our code....
routes.rb
constraints subdomain: Settings.subdomains.api do # you can ignore this
namespace :api do
namespace :v1 do
resources :maps, only: [] do
collection do
get :world_map
end
end
end
end
maps_controller.rb
module Api
module V1
class MapsController < BaseController
def world_map; end
end
end
end
routes output
world_map_api_v1_maps GET /v1/maps/world_map(.:format) api/v1/maps#world_map {:subdomain=>"api"}
The spelling is really crucial from what I can see.
The directort structure for us:
app/controllers/api/v1/maps_controller.rb
So check those points and this should work because it's standard Rails magic.
I have the following route:
resources :success_criteria, except: :new
The following spec fails:
describe SuccessCriteriaController do
describe 'routing' do
it 'routes to #new' do
expect(get('/success_criteria/new')).to_not be_routable
end
end
end
Failure message:
Failure/Error: expect(get('/posts/new')).to_not be_routable
expected {:get=>"/posts/new"} not to be routable, but it routes to {:controller=>"posts", :action=>"show", :id=>"new"}
The controller looks like this:
class SuccessCriteriaController < InheritedResources::Base
load_and_authorize_resource
end
Why does Rails think that posts/new would point to a post with the ID new? That's not right, is it? Does it maybe have to do with InheritedResources?
I believe that if you don't add a constraint to your show route saying that it will only accept digits, everything you put after posts are mapped to be an id to that route.
That means that if your try to access posts/something it would probably throw an ActiveRecord error showing that it couldn't find the Post with id=something.
To add a constraint, add constraints like this:
resources :success_criteria, except: :new, constraints: { id: /\d+/ }
This is my routes.rb
resources :agreements, param: :customer_number, only: :show, as: 'agreement_from_customer_number'
resources :agreements, only: [:index, :update] do
resources :orders, only: :index
end
This is my AgreementsController
def update
...
end
And this is my rspec test for testing the update action:
before :each do
#agreement = create :agreement
end
it 'updates the correct agreement through the internal API' do
patch agreement_path(#agreement)
end
My application uses ActiveModel, so it has no database. Because of this, the createmethod in the before block simply does the following:
factories/agreement.rb
factory :agreement do
skip_create
id '1'
customer_number '1001'
agreement_id '101'
return_address 'Fakeville, USA'
return_postal_code '2013'
end
The test returns the following error:
ActionController::UrlGenerationError:
No route matches {:id=>#<Agreement:0x007f94ab7b0ce0 #id="1", #agreement_id="101", #customer_number="1001", #return_address="Fakeville, USA", #return_postal_code="2013">} missing required keys: [:id]
And if I change the test to look like this:
it 'updates the correct agreement through the internal API' do
patch agreement_path(#agreement.id)
end
I get the following error:
ActionController::UrlGenerationError:
No route matches {:controller=>"agreements", :action=>"/agreements/1"}
Help :-|
I solved this particular problem by doing the following:
patch :update, agreement: #agreement.attributes
Because the controller expects a hash of values rather than an object literal, this worked :-)