I'm trying to test my namespaced controller and not having much luck. I have the following route setup:
namespace :api do
get 'organization/:id/questions/:number', controller: 'questions', action: 'index', as: 'organization_questions'
end
which produces the following route:
api_organization_questions GET /api/organization/:id/questions/:number(.:format) {:controller=>"api/questions", :action=>"index"}
that route works, and I'm able to successfully make a request to it with the following url: http://localhost:3000/api/organization/1/questions/1234567890
However when I try to make a get request to it in my unit test I get the following error:
No route matches {:controller=>"api/questions", :action=>"/api/organization/1/questions/1234567890"}
my get request looks like this:
get api_organization_questions_path(#organization.id, '1234567890')
Not sure what I'm doing wrong!?
What are you using for testing ? RSpec? The first parameter for the get method is the action. The code below should make the request you want:
describe Api::QuestionsController do
it "should do something" do
get :index, :id => #organization.id, :number => '1234567890'
end
end
Related
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+/ }
I have defined a custom route in
routes.rb
get "packages/city/:location_id", to: "packages#index"
In controller_spec.rb,
get :index
gives this error,
ActionController::UrlGenerationError:
No route matches {:action=>"index", :controller=>"packages"}
How to explicitly specify the custom routes in controller specs?
Perhaps it helps if you declare your route like this?
get "packages/city/:location_id" => "packages#index"
Remember to provide a location_id param in specs, e.g. get :index, location_id: 1.
2017
I tried the solutions above but they did not work. I got:
ArgumentError:
unknown keyword: location_id
It seems that RSpec now requires a params parameter. The corresponding call would look like this:
get(:index, params: { location_id: 123 })
it's because you are not passing location_id
the route is defined to match:
/packages/city/:location_id
so in order to comply with it you need to do something like
get :index, location_id: 1234
Had the same issue with Controller spec:
# rake routes | grep media_order
teacher_work_media_orders PATCH /teacher/works/:work_id/media_orders(.:format) teacher/media_orders#update
when I did:
# spec/controller/teacher/media_orders_controller
patch :update data: {}
I got
Failure/Error: patch :update
ActionController::UrlGenerationError:
No route matches {:action=>"update", :controller=>"teacher/media_orders"}
but when I did
patch :update, work_id: 1234, data: {}
that worked
2019
Was tackling this same error on my controller specs. Tried accepted and follow up solutions, but they also did not work, either led to no method error or persisted the no route match error.
Directly defining the route like in the accepted solution also did not satisfy the errors.
After much searching and some keyboard smashing tests pass.
Things to note
controller is for polymorphic resource
routes are nested within the resources :location, only[:index, :show] do ... end
this is API routes, so JSON only
Solution
let(:location) do
create(:location)
end
shared_examples("a user who can't manage locations") do
describe 'GET #index' do
it 'denies access' do
get :index, params:{location_id: location.locationable.id, format: :json}
expect(response).to have_http_status :unauthorized
end
end
end
So in the end it was a combination of both solutions, but had to put them in the params hash or else it would throw name/no method or route errors
Conclusion
references to the association must be in the params hash
even if controller responds_to :json, it will throw errors no route errors
must include a data hash in your request or no route match errors will appear
Hope this helps,
Cheers!
I have tests like those:
RSpec.describe RestaursController, :type => :controller do
context "GET /restaurs/:id" do
before do
#rest = FactoryGirl.create :restaur
end
context "resource exists" do
subject { get "restaurs/#{#rest.id}"}
it { expect(subject).to render_template(:show) }
end
context "resource doesn't exists" do
subject { get "restaurs/#{#rest.id + 1}" }
it { expect(subject).to redirect_to(:root) }
end
end
end
When I run those tests RSpec says:
Failure/Error: subject { get "restaurs/#{#rest.id}"}
ActionController::UrlGenerationError:
No route matches {:action=>"restaurs/7", :controller=>"restaurs"}
But I think my routes are OK. Look on rake routes log
Prefix Verb URI Pattern Controller#Action
restaurs GET /restaurs(.:format) restaurs#index
POST /restaurs(.:format) restaurs#create
new_restaur GET /restaurs/new(.:format) restaurs#new
edit_restaur GET /restaurs/:id/edit(.:format) restaurs#edit
restaur GET /restaurs/:id(.:format) restaurs#show
PATCH /restaurs/:id(.:format) restaurs#update
PUT /restaurs/:id(.:format) restaurs#update
DELETE /restaurs/:id(.:format) restaurs#destroy
root GET / restaurs#index
Have you got any ideas? What is the problem? Maybe it is about RSpec syntax or something like that?
That isn't how you get actions in a controller test. The argument to get is the name of the action, not a routable path. Routing isn't involved in testing controllers, and neither is rake routes.
You're giving it the action name "restaurs/7", which obviously isn't the real name of a method on your controller. Instead, you should be using get :show to invoke the actual method "show" on your controller, and then passing a hash of parameters:
get :show, id: #rest.id + 1
Similarly, you would use get :index, not get "/", and you would use get :edit, id: ..., not get "/restaurs/#{...}/edit". It's the actual method your meant to test on your controller, not whether a route actually leads you to the correct method.
As a related aside, your opening context is out of place. You shouldn't be using GET /restaurs:/:id as a context. Routing isn't involved here. Just test the method names.
How do I unit test a controller method that is called via a custom route?
The relevant route is:
/auth/:provider/callback(.:format) {:controller=>"sessions", :action=>"create"}
On the spec for the SessionsController I can't just use get :create since that route doesn't exist. If I also use get /auth/facebook/callback/ it'll tell me that No route matches {:controller=>"sessions", :action=>"/auth/facebook/callback"}.
It also seems like I can't just use controller.create since #create accesses some keys from the request hash and it also redirects to another path, even if I set request.env['something'] in the spec file.
A functional test should test the function of each action
(given a set of parameters)
Crucially, you should keep your functional tests decoupled from your routes.
(else what's the point in the routing abstraction anyway)
In test::unit a functional test looks something like this
test "#{action_name} - does something" do
#{http_verb} :#{action_name}, :params => {:their => "values"}
assert_response :#{expected_response}
end
Note, we don't mention routing anywhere.
So a real example for your create
test "create - creates a session" do
get :create, :provider => "your provider"
assert_response :success
end
Rails will choke if it can't match a route to this request.
If this doesn't work I suggest you check two things
"get" is the correct http verb
there are no other required parameters in your route (I can see :provider is one)
If I'm doing anything wacky with routing,
I normally add a separate test.
test "create - routing" do
assert_recognizes({
:controller => "sessions",
:action => "create",
:provider => "yourProvider"
}, "/auth/yourProvider/callback")
end
As long as this matches up with your action test
all should be well.
I'm making a simple test project to prepare myself for my test.
I'm fairly new to nested resources, in my example I have a newsitem and each newsitem has comments.
The routing looks like this:
resources :comments
resources :newsitems do
resources :comments
end
I'm setting up the functional tests for comments at the moment and I ran into some problems.
This will get the index of the comments of a newsitem. #newsitem is declared in the setup ofc.
test "should get index" do
get :index,:newsitem_id => #newsitem
assert_response :success
assert_not_nil assigns(:newsitem)
end
But the problem lays here, in the "should get new".
test "should get new" do
get new_newsitem_comment_path(#newsitem)
assert_response :success
end
I'm getting the following error.
ActionController::RoutingError: No route matches {:controller=>"comments", :action=>"/newsitems/1/comments/new"}
But when I look into the routes table, I see this:
new_newsitem_comment GET /newsitems/:newsitem_id/comments/new(.:format) {:action=>"new", :controller=>"comments"}
Can't I use the name path or what I'm doing wrong here?
Thanks in advance.
The problem is in the way your test specifies the URL. The error message is:
No route matches {:controller=>"comments", :action=>"/newsitems/1/comments/new"}
and of course there is no action called "/newsitems/1/comments/new". You want to pass the hash { :controller => :comments, :action => :new, :news_item_id => 1 }.
The right syntax is simply:
get :new, :news_item_id => 1
(Assuming Rails 3)
Try this in your routes.rb
GET 'newsitems/:newsitem_id/comments/new(.:format)' => 'comments#new', :as => :new_newsitem_comment