Rails Functional Test of Arbitrary or Custom URLs - ruby-on-rails

I have a RESTful resource in my Rails app called "Photo". I'm using Paperclip to serve different "styles" of my photos (for thumbnails and the like), and I'm using a custom route to RESTfully access those styles:
map.connect "photos/:id/style/*style", :controller => "photos", :action => "show"
That's working fine, but I want to write a test to make sure it stays that way.
I already have a functional test to call the Photo controller's show action (generated by scaffold in fact):
test "should show photo" do
get :show, :id => photos(:one).to_param
assert_response :success
end
That tests the execution of the action at the URL "/photo/1". Now I want to test the execution of the URL "/photo/1/style/foo". Unfortunately, I can't seem to get ActionController::TestCase to hit that URL; the get method always wants an action/id and won't accept a URL suffix.
How do I go about testing a custom URL?
Update
While checking on #fernyb's answer I found this snippet in the same rdoc
In tests you can simply pass the URL or named route to get or post.
def send_to_jail
get '/jail'
assert_response :success
assert_template "jail/front"
end
However, when I actually try that, I get an error message:
test "should get photo" do
get "/photos/1/style/original"
assert_equal( "image/jpeg", #response.content_type )
end
ActionController::RoutingError: No route matches {:action=>"/photos/1/style/original", :controller=>"photos"}
I wonder if I'm doing something wrong.

Use assert_routing to test routes:
assert_routing("/photos/10/style", :controller => "photos", :action => "show", :id => "10", :style => [])
assert_routing("/photos/10/style/cool", :controller => "photos", :action => "show", :id => "10", :style => ["cool"])
assert_routing("/photos/10/style/cool/and/awesome", :controller => "photos", :action => "show", :id => "10", :style => ["cool", "and", "awesome"])
In your integration test you can then do:
test "get photos" do
get "/photos/10/style/cool"
assert_response :success
end

From the Rails API documentation:
Route globbing
Specifying *[string] as part of a
rule like:
map.connect '*path' , :controller => 'blog' , :action => 'unrecognized?'
will glob all remaining parts of the
route that were not recognized
earlier. The globbed values are in
params[:path] as an array of path
segments.
So it looks like you need to pass the :path arguments, to test the action correctly.

Related

Rails -- namespaced routing to path helpers

I have the following route rule:
get '/reports/stats_by_date', to: 'reports#stats_by_date'
And rake routes gives me this:
reports_stats_by_date GET /reports/stats_by_date(.:format) reports#stats_by_date
That looks correct. So I'm trying to test my controller from within rake with this function:
def test_reports_should_load
get :reports_stats_by_date, :start_date => '2013-10-01', :end_date => '2013-10-05', :format => :json
assert_response :success
end
Pretty simple. But when I run rake test, I get the following error:
ReportsControllerTest#test_reports_should_load:
ActionController::UrlGenerationError: No route matches {:start_date=>"2013-10-01", :end_date=>"2013-10-05", :format=>:json, :controller=>"reports", :action=>"reports_stats_by_date"}
test/controllers/reports_controller_test.rb:6:in `test_reports_should_load'
For some reason, Rails is trying to load up the action :reports_stats_by_date when the route clearly points to reports#stats_by_date. What did I do wrong here?
I think it's because you only need to reference the action name, not the name of the path. stats_by_date instead of reports_stats_by_date
def test_reports_should_load
get :stats_by_date, :start_date => '2013-10-01', :end_date => '2013-10-05', :format => :json
assert_response :success
end

RSpec tests for custom username routes are failing

I have a constrained route that matches usernames like this:
controller :users, :path => '/:username', :as => :user, :constrain => { :username => /^_?[a-z]_?(?:[a-z0-9]_?)*$/i } do
# lots of nested routes go here
end
When I go to write RSpec tests for this (versus using user_id like normal), all the tests are failing because it "can't find the route" even though it works fine on the server.
describe "for an invalid request" do
it "should render a 404 if an associated photo is not found" do
# give it a bad photo id
xhr :post, :destroy, :id => "999999", :photo_id => "999999", :username => #photo_owner.username
# not found
response.status.should == not_found
end
end
This test was working fine when I was using the user_id in my routes prior to switching to usernames:
resources :users do
# nested routes
end
and
xhr :post, :destroy, :id => "999999", :photo_id => "999999", :user_id => #photo_owner.id
So what am I doing wrong and what has changed?
My server console shows this which means I should have all of the parameters passed in properly:
Processing by TagsController#destroy as JS
Parameters: {"constrain"=>{"username"=>/^_?[a-z]_?(?:[a-z0-9]_?)*$/i}, "username"=>"rubynewb", "photo_id"=>"2004-the-title-of-the-photo-here", "id"=>"1797"}
Use :constraints => {...} in your route definition.
You have one too many parameters being passed...
"constrain"=>{"username"=>/^_?[a-z]_?(?:[a-z0-9]_?)*$/i}
Rails doesn't recognize :constrain, therefore it and it's contents are passed along as a parameter instead of being processed by the Rails router.

Why to add a connection in routes file when using link_to in rails 2

I was trying to accomplish the following:
<%= link_to "Log out", { :controller
=> 'users', :action => 'logout' }, :class => 'menulink2' %>
But it didn't work, it always redirected me to a show view. I had to had the following to my routes.rb:
map.connect 'users/logout',
:controller => 'users', :action =>
'logout'
Why didn't rails recognize the action I was passing ('logout') ?
That logic has to be specified somewhere. There's got to be some mapping from the hash {:controller => 'users', :action => 'logout'} to a url, and the place that's done in rails is the routes.rb file. In older versions of rails many routes.rb came with a default at the end:
map.connect ':controller(/:action/(:id(.:format)))'
Which would make it so that most any :controller, :action hash could be specified and then routed to host.url/:controller/:action.
With the more modern versions resource-based routes are heavily favored, and controllers which don't follow rails' REST conventions (i.e. having only :index,:show,:create,:new,:edit,:update,:destroy methods) generally have to have their routes explicitly specified in some way.
(Either with map.resources :users, :collection => {:get => :logout} or with map.connect( 'some_url', :controller => 'users', :action => 'logout'}))
I'm guessing, but the reason they did that is probably that the actions of a controller are really just its public methods.
It's frequently nice to have public methods in your controllers that aren't url-end-points for testing purposes.
For instance, you could have before_filters as public methods that you might want to test without having to use #controller.send(:your_before_filter_method) in your test code.
So they whitelist the resource actions, and make the others unreachable by default. Let me look through the rails changelog and see if I'm right.

Testing a named route in rails

I have the following route defined in routes.rb:
map.search 'fuzzyleads/search/:url', :controller => 'fuzzyleads', :action => 'search', :method => 'get'
The problem is that I cannot get this test to pass:
def test_search_route
assert_generates "fuzzyleads/search/someurl", { :controller => "fuzzyleads", :action => "search", :url => "someurl" }
end
It does not like the url part, I get the following error:
found extras <{:url=>"someurl"}>, not
<{}>
I have no idea why, can anyone see what I am doing wrong?
The output of rake routes would help us make a little better guess. Here's how I tested using rails-2.3.8:
(1) Added your route as the second route in config/routes.rb
(2) Created the following rspec test:
it "should have valid route for fuzzyleads search" do
route_for(:controller => 'fuzzyleads', :action => 'search', :url => 'someurl').should ==
"/fuzzyleads/search/someurl"
end
But this failed with the following:
ActionController::RoutingError in 'FuzzyLeadsController should have valid route for fuzzyleads search'
No route matches "/fuzzyleads/search/someurl" with {:method=>:get}
If you're expecting this failure, we suggest {:get=>"/fuzzyleads/search/someurl"}.should_not be_routable
But when I removed the :method restriction everything worked successfully. Adding :method => 'get' wasn't sufficient to make the test pass. I've seen similar failures when I've set conditions on routes that haven't been met, so I'd guess something similar is happening in your case.

Creating a Custom Rails Route

I am attempting to create a custom route in rails and am not sure if I am going about it in the right way. Firstly I have a RESTful resource for stashes that redirects to mystash as the controller:
map.resources :stashes, :as => 'mystash'
site.com/mystash goes to :controller => 'stashes', :action => 'show'
Which is what I want. Now is where it gets somewhat confusing. I would like to be able to add conditional params to this route. Ultimately I would like to have a route that looks like this:
site.com/mystash/zoomout/new/quiz_on/
I have places this in routes:
map.connect 'mystash/:zoom/:nav_option/:quiz',
:controller => 'stashes',
:action => 'show'
map.connect 'mystash/:zoom/:nav_option',
:controller => 'stashes',
:action => 'show'
map.connect 'mystash/:zoom',
:controller => 'stashes',
:action => 'show'
map.connect 'mystash',
:controller => 'stashes',
:action => 'show'
My routes have ended up looking like this in the browser:
site.com//mystash/zoomin?nav_option=New&quiz=quizon
and this is what one of my links looks like:
<%= link_to "In", stash_path("zoomin", :nav_option => #nav_option, :quiz => #quiz) %>
Any help is appreciated, I am pretty new to custom routes!
You should be giving these routes different names instead of the default, or you should be specifying your route with a hash and not a X_path call. For instance:
map.stash_zoom_nav_quiz 'mystash/:zoom/:nav_option/:quiz',
:controller => 'stashes',
:action => 'show'
map.stash_zoom_nav 'mystash/:zoom/:nav_option',
:controller => 'stashes',
:action => 'show'
Keep in mind that when you declare a named route, the parameters in the path must be specified in the X_path call with no omissions, and not as a hash.
link_to('Foo', stash_zoom_nav_quiz_path(#zoom, #nav_option, #quiz))
link_to('Bar', stash_zoom_nav_path(#zoom, #nav_option))
The alternative is to not bother with named routes and let the routing engine figure it out on its own:
link_to('Foo', :controller => 'stashes', :action => 'show', :zoom => #zoom, :nav_option => #nav_option, :quiz => #quiz)
If you're uncertain what routes are defined, or how to call them, always inspect the output of "rake routes" very carefully. You can also write functional tests for routes with the assert_routing method.

Resources