Reputation: 80041
I have the following controller test using Minitest::Rails and Rails 4. When I run it, I get an error: ActionController::UrlGenerationError: No route matches {:action=>"/", :controller=>"foo"}
error, despite it being defined.
The whole point of this is to test methods that are on ApplicationController (FooController
exists only in this test and is not a placeholder for the question).
class FooController < ApplicationController
def index
render nothing: true
end
end
class FooControllerTest < ActionController::TestCase
it 'does something' do
with_routing do |set|
set.draw do
root to: 'foo#index', via: :get
end
root_path.must_equal '/' #=> 👍
get(root_path).must_be true #=> No route matches error
end
end
end
There a number of similar questions on StackOverflow and elsewhere, but they all refer to the issue of route segments being left out (e.g. no ID specified on a PUT). This is a simple GET with no params, however.
I get the same result if the route is assembled differently, so I don't think it's the root_path
bit doing it (e.g. controller :foo { get 'test/index' => :index }
).
Upvotes: 1
Views: 1414
Reputation: 80041
The solution to this problem was much simpler than expected:
# change this:
get(root_path)
# to this
get(:index)
The with_routing
method works fine to define the path in this context.
Upvotes: 0
Reputation: 2397
if you check the source of ActionController::TestCase#get
method, it expects action name, e.g. :index, :create, 'edit, 'create'
if you pass root_path
on #get
method, absolutely it will raise error, because root_path
method returns '/'.
I just checked to add :/ method to FooController
class FooController
def index
end
def /
end
end
Rails.application.routes.draw do
root 'foo#index'
get 'foobar' => 'foo#/'
end
when I was visiting http://localhost:3000/foobar, rails gave me
AbstractController::ActionNotFound (The action '/' could not be found for FooController):
respond
I think '/' is not permitted action on rails, I don't do research further, because I think it's very reasonable.
You may write
assert_routing '/', controller: "foo", action: "index"
for current test, then you can write integration test to check root_path
and other features.
Following are the source code of some methods I've talked above: (I'm using rails version 4.2.3 to test this interesting issue)
# Simulate a GET request with the given parameters.
#
# - +action+: The controller action to call.
# - +parameters+: The HTTP parameters that you want to pass. This may
# be +nil+, a hash, or a string that is appropriately encoded
# (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
# - +session+: A hash of parameters to store in the session. This may be +nil+.
# - +flash+: A hash of parameters to store in the flash. This may be +nil+.
#
# You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with
# +post+, +patch+, +put+, +delete+, and +head+.
#
# Note that the request method is not verified. The different methods are
# available to make the tests more expressive.
def get(action, *args)
process(action, "GET", *args)
end
# Simulate a HTTP request to +action+ by specifying request method,
# parameters and set/volley the response.
#
# - +action+: The controller action to call.
# - +http_method+: Request method used to send the http request. Possible values
# are +GET+, +POST+, +PATCH+, +PUT+, +DELETE+, +HEAD+. Defaults to +GET+.
# - +parameters+: The HTTP parameters. This may be +nil+, a hash, or a
# string that is appropriately encoded (+application/x-www-form-urlencoded+
# or +multipart/form-data+).
# - +session+: A hash of parameters to store in the session. This may be +nil+.
# - +flash+: A hash of parameters to store in the flash. This may be +nil+.
#
# Example calling +create+ action and sending two params:
#
# process :create, 'POST', user: { name: 'Gaurish Sharma', email: '[email protected]' }
#
# Example sending parameters, +nil+ session and setting a flash message:
#
# process :view, 'GET', { id: 7 }, nil, { notice: 'This is flash message' }
#
# To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE+ and +HEAD+ requests
# prefer using #get, #post, #patch, #put, #delete and #head methods
# respectively which will make tests more expressive.
#
# Note that the request method is not verified.
def process(action, http_method = 'GET', *args)
# .....
end
Upvotes: 2
Reputation: 5110
I did some search on what information you have provided. I found an issue open in rspec-rails
gem. However gem doesn't matter here but fundamentally they said its context problem. When you call with_routing
it doesn't executed in correct context, so gives error of No Route matches.
To resolve issue, I tried locally with different solution. Here is what I have tried
require 'rails_helper'
RSpec.describe FooController, type: :controller do
it 'does something' do
Rails.application.routes.draw do
root to: 'foo#index', via: :get
end
expect(get: root_path).to route_to("foo#index")
end
end
In above code, the major problem is it overwrite existing routes. But we can reproduce routes with Rails.application.reload_routes!
method.
I hope this helps to you!!
UPDTATE
I tried to understand your last comment and dig into get
method. When we call get
method it takes argument of action of controller for which we are doing test. In our case when we do get(root_path)
it tries to find foo#/
which is not exists and hence gives no route matches error.
as our main goal is to check root_path routes are generated correctly, we need to use method assert_routing
to check it. Here is how I test and it works
assert_routing root_path , controller: "foo", action: "index"
Full code :
require 'test_helper'
class FooControllerTest < ActionController::TestCase
it 'does something' do
with_routing do |set|
set.draw do
root to: 'foo#index', via: :get
end
root_path.must_equal '/' #=> true
assert_routing root_path , controller: "foo", action: "index" #=> true
get :index
response.body.must_equal ""
end
end
end
I read things from official document : http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html
Upvotes: 2
Reputation: 4639
The whole point of this is to test behavior inherited by the ApplicationController.
There is no behavior in your question related to ApplicationController
other than the fact that FooController
inherits from ApplicationController
. And since there's no other behavior to test here in FooController
related to something from the Application Controller
...
You can test this
class FooController < ApplicationController
def index
render nothing: true
end
end
with this
describe FooController, type: :controller do
describe '#index' do
it 'does not render a template' do
get :index
expect(response).to render_template(nil)
end
end
end
Upvotes: 0