views:

472

answers:

3

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?

A: 

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.

Matt Haley
I had thought about that, but I was really hoping to avoid it. The actual use case is the URL being in a certain format; passing in the path segments as a parameter bypasses that check.
Craig Walker
Are you trying to test the framework? When you `get /photos/1/styles/foo', it's up to Rails to parse the URL and call the controller. You shouldn't need to worry about that, just pass the expected parameters to the action that Rails will call it with.
Matt Haley
+2  A: 

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
fernyb
That's useful stuff, but it doesn't actually call the action; it just checks that the routing of the URLs will result in the particular action/params. (I'll edit my question to clarify what I'm looking for).
Craig Walker
A: 

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.

Craig Walker
The above code will only work as an integration test (script/generate integration_test photos_style) try it and see it work. It will not work as a functional test.
fernyb
Yup, integration testing was the key.
Craig Walker