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.

Functional tests don't care about http-verb that you use on the simulate request.

#5977

Context:

Rails: 3.0.1
Ruby: 1.8.7

In case I have these routes:

resources :my_things do
  collection do 
    get :my_get_action
    post :my_post_action
  end
end

I can simulate this request in my functional test without any warning:

# my_things_controller_test.rb
post(:my_get_action)
get(:my_post_action)

What it looks like is that it doesn't matter which request type do you use.

See this very simple Rails project that reproduce this behavior in its functional tests:

These are the tests I'm trying:

require 'test_helper'

class MyThingsControllerTest < ActionController::TestCase
  test "should respond to the get action" do
    get(:my_get_action)

    assert_response :success
    assert_equal("my get action", @response.body)
  end

  test "should respond to the post action" do
    post(:my_post_action)

    assert_response :success
    assert_equal("my post action", @response.body)
  end

  test "should respond to the get action with post verb" do
    assert_raise(ActionController::RoutingError) do
      post(:my_get_action)
    end
  end

  test "should not respond to the post action with get verb" do
    assert_raise(ActionController::RoutingError) do
      get(:my_post_action)
    end
  end
end

This is the result:

$ rake test:functionals
(in /Users/fguillen/Develop/Rails/Contribute/TestingRoutesVerb)
Loaded suite /Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader
Started
F.F.
Finished in 0.180686 seconds.

  1) Failure:
test_should_not_respond_to_the_post_action_with_get_verb(MyThingsControllerTest) [/test/functional/my_things_controller_test.rb:25]:
<ActionController::RoutingError> exception expected but none was thrown.

  2) Failure:
test_should_respond_to_the_get_action_with_post_verb(MyThingsControllerTest) [/test/functional/my_things_controller_test.rb:19]:
<ActionController::RoutingError> exception expected but none was thrown.

4 tests, 6 assertions, 2 failures, 0 errors
rake aborted!
Command failed with status (1): [/System/Library/Frameworks/Ruby.framework/...]

Reported by Fernando Guillen · November 15th, 2010 @ 03:09 PM

State: invalid
Milestone: none
Assigned to: nobody
Importance: Low

Activity

  1. Andrew White
    Andrew White
    • State changed from new to invalid
    • Importance changed from to Low

    Functional tests call the controller action directly and don't go through the router. Use an ActionDispatch::IntegrationTest to check the routing constraints. e.g:

      rails generate test_unit:integration my_controller_routing
    

    November 17th, 2010 @ 05:49 PM

  2. Fernando Guillen
    Fernando Guillen

    But then what is the propose of having the five verbs in the ActionController::TestCase if I can use any of them without any restriction or diference?

    It is not intuitive.

    And besides the ActionController::TestCase, some way, looks to the defined routes because if don't have the route defined on your routes.rb it raises an ActionController::RoutingError.

    November 17th, 2010 @ 08:09 PM

  3. Andrew White
    Andrew White

    The get, post, etc. methods are just wrappers around the process method of ActionController::TestCase. All they do is set the REQUEST_METHOD header in the Rack environment so that any code in your controller that is dependent on the request method acts appropriately, e.g.

      # routes.rb
      match '/basket', :to => 'shop#basket', :via => [:get, :post]
    
      # app/controllers/shop_controller.rb
      class ShopController < ApplicationController
        before_filter :load_basket
    
        def basket
          @basket.update(params[:basket]) if request.post?
        end
    
        protected
          def load_basket
            @basket = Basket.new(session[:basket])
          end
      end
    

    The routing error you get is because the process method builds a request uri so it's a generation error and not a recognition error (generation of urls ignores HTTP verbs as well).

    Functional tests are meant to be isolated tests of your controller - if you need to test the full stack use an integration test.

    November 18th, 2010 @ 07:05 AM

  4. Fernando Guillen
    Fernando Guillen

    Ok, I understand pretty much better all this behavior.

    So the verbs in the ActionController::TestCase are just to mark this request with this REQUEST_METHOD header in order to be used (or not) in our Controller.

    And also I have realized that we are also checking that the url we are calling can be built based on our routes definition, but without taken care about the verbs restrictions. So it should be able to be generated but it doesn't need to be recognized.

    (This last point sounds a few inconsistent for me, but I understand I don't have the whole picture in my mind)

    Thanks for your explanation.

    November 18th, 2010 @ 09:58 AM

  5. Andrew White
    Andrew White

    That's pretty much it except the route is being built so that the REQUEST_URI header can be set and not to check whether the route can be generated (though it obviously it needs to be).

    November 18th, 2010 @ 10:29 AM