3
\$\begingroup\$

When integration testing Rails applications HTTP request helpers can take a string or a named route as the first argument.

I find myself writing:

test "user visits edit page" do
  homer = users(:homer)
  get "users/#{homer.id}/edit"
  assert_response :success
end

But I can also write:

test "user visits edit page" do
  homer = users(:homer)
  get edit_user_path(homer)
  assert_response :success
end

So which style should I use? And why is it preferable to the other?

\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

Use the 2nd version - i.e. use the path helper methods, don't use strings. Or use a 3rd one, detailed below.

Ideally, you test your routes separately from the controller (in test/routes//spec/routes/ files). That'll test that "/users/homer/edit" indeed routes to the users#edit action. Provided your using Rails' standard resourceful routes, you don't need further tests. Anything more would be you testing that Rails works, which should be unnecessary. You want to test your app.

Secondly, the reason Rails provides path/url helper methods is that hard-coding paths/urls is a recipe for a headache should the representation of those paths ever change. For instance, a simple resources :users, as: :people in the routes file would break all your hard-coded "/users/..." paths.

Thirdly, you're simply writing homer.id. But path helpers call to_param (which you can override in your models) on the object they're passed. So you're not accurately mimicking the way the path helper methods work.

Basically, if you make a change in routes.rb, you don't want to have to update your controller tests - after all, you didn't necessarily change your controllers! You only want to update your routing tests, if you have any.

Alternatively, (edit: this is for controller tests, specifically) you can use a third version, where you specify action and params separately:

get :edit, { id: homer.to_param }

This way, you side-step both hard-coded URLs, route matching, and path/URL helpers entirely. This is probably the "cleanest" solution.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks! But in the third scenario, how would the test know the right controller? If it can be inferred from the user, what about :new where no user object is used? If it's from the name of the test, I found that their names aren't not usually anologous to controllers. \$\endgroup\$
    – Mohamad
    Commented Jan 26, 2015 at 17:39
  • \$\begingroup\$ @Mohamad Right, sorry - I was thinking controller tests (where the controller is derived from the test class/spec name), rather than high-level integration tests. \$\endgroup\$
    – Flambino
    Commented Jan 26, 2015 at 17:43
1
\$\begingroup\$

Even though resources :users, as: :people will still break helpers too (users_path becomes people_path), it's much easier to find & replace helper methods, than hard-coded strings.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.