Indeed, the two code are equivalent conceptually.
They are doing the same thing by issuing a GET
request for users/edit.html.erb
via edit
action in Userscontroller
.
However the contexts in which you are testing the above behaviour are different.
The code
get :edit, id: @user
is in the context of testing a controller (UsersController
). Therefore you only need to specify the action ("edit" in this case) along with necessary parameters("id" in this case).
get edit_user_path(@user)
is in the context of simulating a user browsing your app, which you can do via integration tests. Here you need to be precise about the routing path since there can be different controllers involved. That is, the context in which an action is called is not restricted to one particular controller.
For example, in Listing 8.40, we have an integration test that simulates a user logging out:
test "login with valid information followed by logout" do
get login_path
post login_path, session: { email: @user.email, password: 'password' }
assert is_logged_in?
assert_redirected_to @user
follow_redirect!
assert_template 'users/show'
assert_select "a[href=?]", login_path, count: 0
assert_select "a[href=?]", logout_path
assert_select "a[href=?]", user_path(@user)
delete logout_path
assert_not is_logged_in?
assert_redirected_to root_url
# Simulate a user clicking logout in a second window.
delete logout_path
follow_redirect!
assert_select "a[href=?]", login_path
assert_select "a[href=?]", logout_path, count: 0
assert_select "a[href=?]", user_path(@user), count: 0
end
In this integration test, there are two controllers involved: UsersController
and SessionsController
.
Specifically:
user_path(@user)
in assert_select "a[href=?]",
user_path(@user)
is about a show
action in UsersController
.
login_path
in get login_path
calls a new
action in
SessionsController
.
login_path
in post login_path, session: { email: @user.email,
password: 'password' }
calls a create
action in
SessionsController
.
login_path
in assert_select "a[href=?]", login_path
is about
a new
action in SessionsController
.
logout_path
in assert_select "a[href=?]", logout_path
is
about a destroy
action in SessionsController
.
This is possible in integration tests simply because we are not restricted to a particular controller when calling some actions.
This also explains why the first code you provided
get :edit, id: @user
throws an error in integration tests.
It's not specifying in what controller the edit
action is called. (It could be the edit
action in AccountActivationsController
or PasswordResetsController
, for example)
Also, the point you raised
i) calls the controller action, whereas ii) routes to the resource
is important.
Controller tests are purely about testing a controller by issuing rather "low-level", or "machine-level", commands. That is, we submit particular HTTP requests (PATCH
in your case) directly to controller actions(edit
in your case) without being concerned about simulating a user browsing your app (i.e. user actually clicking a link "Edit Profile"), which you can do with more "high-level" integration tests.