Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

[PATCH] assert_recognizes does not support constraints

#5805

In a Rails 3.0.0 app with a HomeController and an #index action:

# routes.rb
class FooConstraint
  def self.matches?(request)
    false  # Ensures routes with this constrains should _never_ match
  end
end

Constraints::Application.routes.draw do
  constraints FooConstraint do
    root :to => 'home#index'
  end
end
# home_controller_test.rb
require 'test_helper'
class HomeControllerTest < ActionController::TestCase
  test "should route to home" do
    assert_recognizes({ :controller => "home", :action => "index" }, "/")
  end
end

This test should fail, as it does when I try to hit / in my web browser ('No route matches "/"').

It seems the code in FooConstrains.matches? is never run.

Reported by ms (at budstikka) · October 14th, 2010 @ 12:27 PM

State: resolved
Milestone: 3.x
Assigned to: Andrew White Andrew White
Importance: Low

Activity

  1. Ryan Bigg
    Ryan Bigg
    • State changed from new to incomplete
    • Importance changed from to Low

    Please attach a failing test case to this ticket as per the Contributing to Rails guide that can be found at http://guides.rubyonrails.org.

    October 14th, 2010 @ 12:33 PM

  2. ms (at budstikka)
    ms (at budstikka)

    I haven't been able to make a failing test case, as there is no assert_not_recognizes method. The test case in the attached patch should fail, but it does not. I couln't find any test for the routing assert_* methods themselves. I'd be happy to write more correct tests with a little guidance.

    The main point here is that assert_recognizes does not seem to restrict the routes when the tests are run in the way the constraints do when the code is run in the app and accessed from a web browser.

    October 15th, 2010 @ 11:41 AM

  3. Ryan Bigg
    Ryan Bigg
    • Tag cleared.

    Automatic cleanup of spam.

    October 19th, 2010 @ 08:32 AM

  4. Dim
    Dim

    Here's a working (in this case, failing) patch.

    November 8th, 2010 @ 01:26 PM

  5. Dim
    Dim

    Apologies, please ignore the last post. Here's an even better patch (not sure if it's the best way to validate constraints though).

    November 8th, 2010 @ 01:37 PM

  6. Dim
    Dim

    Ok, this is my final go - I promise!

    November 8th, 2010 @ 02:02 PM

  7. Dim
    Dim
    • Title changed from assert_recognizes does not support constraints to [PATCH] assert_recognizes does not support constraints

    November 11th, 2010 @ 03:49 PM

  8. Brian Ploetz
    Brian Ploetz

    Actually, I seem to be able to test some constraints, but not others. Section 3.9 of the Routing guide says:

    3.9 Request-Based Constraints

    You can also constrain a route based on any method on the Request object that returns a String.

    I'm able to test a constraint on :format, but not on :protocol. For example:

    # config/routes.rb
    ProtocolBug::Application.routes.draw do
      scope :constraints => { :format => /(json|xml)/ } do
        match '/foos.(:format)' => 'foos#index', :via => :get
      end
    end
    
    # app/controllers/foos_controller.rb
    class FoosController < ApplicationController
      # GET /foos?...
      def index
        render :nothing => true
      end
    end
    
    # test/functional/foos_controller_test.rb
    require 'test_helper'
    class FoosControllerTest < ActionController::TestCase
      test "should route to index" do
        assert_recognizes({ :controller => "foos", :action => "index", :format => "xml" }, "/foos.xml")
      end
    
      test "should fail to route to index" do
        assert_recognizes({ :controller => "foos", :action => "index", :format => "bogus" }, "/foos.bogus")
      end
    end
    

    And when I run the functional tests the test with the bogus format fails (as MS notes above, there's no "assert_not_recognized", so I'm just letting it fail here for you to see that the routing constraint is working correctly).

    [bploetz:~/workspace/protocol_bug]> rake test:functionals

    Started

    E.

    Finished in 0.027135 seconds.

    1) Error: test_should_fail_to_route_to_index(FoosControllerTest): ActionController::RoutingError: No route matches "/foos.bogus" /Users/bploetz/.rvm/gems/ruby-1.9.2-p0@server/gems/actionpack-3.0.3/lib/action_dispatch/routing/route_set.rb:523:in recognize_path'

    /Users/bploetz/.rvm/gems/ruby-1.9.2-p0@server/gems/actionpack-3.0.3/lib/action_dispatch/testing/assertions/routing.rb:211:in recognized_request_for'

    /Users/bploetz/.rvm/gems/ruby-1.9.2-p0@server/gems/actionpack-3.0.3/lib/action_dispatch/testing/assertions/routing.rb:44:in assert_recognizes' test/functional/foos_controller_test.rb:8:inblock in <class:FoosControllerTest>'

    2 tests, 1 assertions, 0 failures, 1 errors, 0 skips

    However, if I add a constraint on :protocol => "https://", it does not honor it in the test.

    # config/routes.rb
    ProtocolBug::Application.routes.draw do
      scope :constraints => { :protocol => "https://" } do
        match '/foos' => 'foos#index', :via => :get
      end
    end
    
    # test/functional/foos_controller_test.rb
    require 'test_helper'
    class FoosControllerTest < ActionController::TestCase
      test "should route to index" do
        assert_recognizes({ :controller => "foos", :action => "index", :protocol => "https://" }, "/foos")
      end
    end
    

    [bploetz:~/workspace/protocol_bug]> rake test:functionals

    Started

    E

    Finished in 0.007458 seconds.

    1) Error: test_should_route_to_index(FoosControllerTest):
    ActionController::RoutingError: No route matches "/foos"
    /Users/bploetz/.rvm/gems/ruby-1.9.2-p0@server/gems/actionpack-3.0.3/lib/action_dispatch/routing/route_set.rb:523:in recognize_path' /Users/bploetz/.rvm/gems/ruby-1.9.2-p0@server/gems/actionpack-3.0.3/lib/action_dispatch/testing/assertions/routing.rb:211:inrecognized_request_for' /Users/bploetz/.rvm/gems/ruby-1.9.2-p0@server/gems/actionpack-3.0.3/lib/action_dispatch/testing/assertions/routing.rb:44:in assert_recognizes' test/functional/foos_controller_test.rb:4:inblock in <class:FoosControllerTest>'

    1 tests, 0 assertions, 0 failures, 1 errors, 0 skips

    And the protocol constraint seems to be correct according to rake routes:

    [bploetz:~/workspace/protocol_bug]> rake routes

    foos GET /foos(.:format) {:protocol=>"https://", :controller=>"foos", :action=>"index"}

    I don't know if this is a different issue than this one, but figured I'd point it out.

    December 13th, 2010 @ 05:42 PM

  9. Steve Schwartz
    Steve Schwartz

    What is the status of this ticket? I am also encountering this problem, in that the routing constraint works fine, but the specs fail when they should be passing. I'd like to help fix it if the patch still needs work.

    February 12th, 2011 @ 08:38 PM

  10. Andrew White
    Andrew White

    In terms of testing a constraint against a protocol you have to pass a full url to assert_recognizes, e.g:

    test "should route to index when using https" do
      assert_recognizes({ :controller => "foos", :action => "index" }, "https://test.host/foos")
    end
    

    For testing that a route fails to recognize then wrap the assert_recognizes in an assert_raises, e.g.:

    test "should not route to index when using http" do
      assert_raises(ActionController::RoutingError) do
        assert_recognizes({ :controller => "foos", :action => "index" }, "http://test.host/foos")
      end
    end
    

    There is an issue with class based constraints as the recognize_path method in ActionDispatch::Routing::RouteSet bypasses the constraints app. However I think I can fix that with a bit of refactoring of ActionDispatch::Routing::Mapper::Constraints so that I can check a constraint without doing a call.

    Be aware though this is a functional test method - if there's a dependency in your class based constraint that needs the full application running then you're best off using an integration test, RSpec request spec or Cucumber feature for checking the constraint is working.

    February 13th, 2011 @ 04:40 PM

  11. Andrew White
    Andrew White
    • State changed from incomplete to open
    • Milestone set to 3.x
    • Assigned user set to Andrew White

    February 13th, 2011 @ 04:48 PM

  12. Repository
  13. Repository