In Rails 3, when writing functional tests in MiniTest, I got in the habit of testing routes separately from testing my controller actions. I got the idea from the Rails Guide on Testing - Section 9: Testing Routes. However, after upgrading my application to Rails 4 I noticed that the controller action tests themselves have started balking about unknown routes if I don't supply the get|patch|post|delete
method with a proper set of params.
For example, given routes:
# config/routes.rb
namespace "api" do
namespace "v2", defaults: { format: :json } do
resources :users do
resources :posts do
resources :comments
end
end
end
end
And functional test:
# test/controllers/api/v2/comments_controller_test.rb
describe Api::V2::CommentsController
it "does something" do
get :index
end
end
In Rails 3, the above would work. But in Rails 4 I get a URL generation error:
ActionController::UrlGenerationError: No route matches {:action=>"index", :controller=>"api/v2/comments"}
From this I can infer that the get
helper simply failed to match a route from the routes file when trying to locate the controller and action. Fair enough. I can fix this by changing the get
call to include the parameters needed to satisfy the nested route, like this:
# test/controllers/api/v2/comments_controller_test.rb
describe Api::V2::CommentsController
it "does something" do
get :index, { user_id: "1", post_id: "1" }
end
end
... then all is well again.
So my question is, since it wasn't the case in in Rails 3, is it now OK to trust the controller action tests to fully test/validate my routes in Rails 4+? Or is there additional advantage in testing the routes as well still? Is there, perhaps, some other angle that the route tests cover that the controller action tests don't cover? (Note: I'm not asking for an opinion on what's good to test; I'm asking for functional differences between route integration tests and controller action tests in regard to route requirements.)
Also, I couldn't find a specific reference to this change in behavior in the Rails 4 release notes (or in Minitest), so I'm wondering why this behavior change was made in the first place. I don't think it's a bad thing -- I think it's good -- but I feel weird not seeing it mentioned in a changelog somewhere. And I thought that half the point of the get|patch|post|delete
methods were to free you from having to think about what params are needed for routing in the first place.
For completeness, here is the route test I would use for this:
describe "CommentsController Route Integration Test" do
let(:default_options) {
{ controller: "api/v2/comments",
user_id: "1",
posts_id: "1",
format: :json }
}
it "#index" do
assert_routing "/api/v2/users/1/posts/1/comments",
default_options.merge(action: "index")
end
end
UPDATE
I've been looking through ActionDispatch code for an answer... the only thing I can see so far is that the url_for
stuff has changed a lot since Rails 3 and that the ActionController::UrlGenerationError class itself has been added in Rails 4. So it could be that these new, more strict routing requirements are incidental change to the decoupling of ActionView and ActionController.