1

I'm going through Hartl's exercises in chapter 9, and got to the part about writing integration test for Requiring logged-in users .

I notice that Listing 9.17 (i) uses

get :edit, id: @user

whereas Listing 9.14 (ii) uses:

get edit_user_path(@user)

What's the difference? For the purpose of testing that logged-in users get redirected to home page if not logged in, the latter works, whereas the former throws an error.

Conceptually, the two statements above look like the same, i) calls the controller action, whereas ii) routes to the resource

Is that right?

1 Answers1

2

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: UsersControllerand 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.

tg_so
  • 496
  • 6
  • 11