This project is archived and is in readonly mode.

#5689 ✓invalid
Frederik Vollert

assert_generates expects unexpected generated_path

Reported by Frederik Vollert | September 23rd, 2010 @ 12:17 PM

The following difficulty concerning assert_generates (defined in actionpack-3.0.0/lib/action_dispatch/testing/assertions/routing.rb:94) occured while using RSpec2 and was discussed as "ruby-related issue" in November '09 (https://rspec.lighthouseapp.com/projects/5645/tickets/907-the-gener....

Perhaps this is not a malfunction, but a misunderstanding of the usage of this assertion as illustrated in http://apidock.com/rails/v3.0.0/ActionDispatch/Assertions/RoutingAs... .

Given I have set up the following routes in Rails 3.0.0 (Ruby 1.9.2):

MyApp::Application.routes.draw do
  match 'logout1' => 'user_sessions#destroy', :as => 'logout1'
  match 'logout2' => 'user_sessions#destroy', :as => 'logout2'
  match 'logout3' => 'user_sessions#destroy'
  match 'login' => 'user_sessions#new', :as => 'login'

  resources :user_sessions
end

The following assertions (excerpt from routing spec) ...

assert_generates "/logout1", :controller => "user_sessions", :action => "new"
assert_generates "/logout2", :controller => "user_sessions", :action => "new"
assert_generates "/logout3", :controller => "user_sessions", :action => "new"
assert_generates "/login", :controller => "user_sessions", :action => "new"

... used e.g. in RSpec produce the following results:

Failures:
  1) General routing recognizes /logout2
     Failure/Error: { :get => "/logout2" }.should route_to(:controller => "user_sessions", :action => "destroy")
     The generated path <"/logout1"> did not match <"/logout2">.
 
  2) General routing recognizes /logout3
     Failure/Error: { :get => "/logout3" }.should route_to(:controller => "user_sessions", :action => "destroy")
     The generated path <"/logout1"> did not match <"/logout3">.
 
  3) General routing recognizes /login
     Failure/Error: { :get => "/login" }.should route_to(:controller => "user_sessions", :action => "new")
     The generated path <"/user_sessions/new"> did not match <"/login">.

The first named route is accepted as generated_path and expected_path both have the value "/logout1". The following two are not recognized, but work fine as does the "login"-route.

In [...]action_dispatch/testing/assertions/routing.rb:86 @routes.generate_extras seems to return the "unexpected" route.

      def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
        if expected_path =~ %r{://}
          begin
            uri = URI.parse(expected_path)
            expected_path = uri.path.to_s.empty? ? "/" : uri.path
          rescue URI::InvalidURIError => e
            raise ActionController::RoutingError, e.message
          end
        else
          expected_path = "/#{expected_path}" unless expected_path.first == '/'
        end
        # Load routes.rb if it hasn't been loaded.

        generated_path, extra_keys = @routes.generate_extras(options, defaults)
        found_extras = options.reject {|k, v| ! extra_keys.include? k}

        msg = build_message(message, "found extras <?>, not <?>", found_extras, extras)
        assert_block(msg) { found_extras == extras }
        msg = build_message(message, "The generated path <?> did not match <?>", generated_path,
            expected_path)
        assert_block(msg) { expected_path == generated_path }
      end

Though they are all four existing as in "rake routes":

logout1        /logout1(.:format)                                  {:action=>"destroy", :controller=>"user_sessions"}
logout2        /logout2(.:format)                                  {:action=>"destroy", :controller=>"user_sessions"}
logout3        /logout3(.:format)                                  {:action=>"destroy", :controller=>"user_sessions"}
login          /login(.:format)                                    {:action=>"new", :controller=>"user_sessions"}
[...]
user_sessions     GET    /user_sessions(.:format)                  {:action=>"index", :controller=>"user_sessions"}
user_sessions     POST   /user_sessions(.:format)                  {:action=>"create", :controller=>"user_sessions"}
new_user_session  GET    /user_sessions/new(.:format)              {:action=>"new", :controller=>"user_sessions"}
edit_user_session GET    /user_sessions/:id/edit(.:format)         {:action=>"edit", :controller=>"user_sessions"}
user_session      GET    /user_sessions/:id(.:format)              {:action=>"show", :controller=>"user_sessions"}
user_session      PUT    /user_sessions/:id(.:format)              {:action=>"update", :controller=>"user_sessions"}
user_session      DELETE /user_sessions/:id(.:format)              {:action=>"destroy", :controller=>"user_sessions"}

I haven't found a ticket concerning assert_generates and saw that Josh worked on this bit of code, so I figured to create a ticket to clarify this issue.

Comments and changes to this ticket

  • Frederik Vollert

    Frederik Vollert September 23rd, 2010 @ 12:18 PM

    • Tag changed from routing assert_generates, generate_extras to routing assert_generates, generate_extras, rails3.0.0
  • Andrew White

    Andrew White September 23rd, 2010 @ 02:16 PM

    • State changed from “new” to “invalid”
    • Assigned user cleared.
    • Importance changed from “” to “Low”

    The assert_generate code tries to generate a url from a set of options - in your case these options are the same so it always generates the first logout route. In the login case the get method constraint of the /user_sessions/new path provides a more specific match so overrides the the /login route.

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Pages