1

I'm a bit stumped on how (and where) to write some rspec tests for the "stay signed in" functionality you see all over, including on the google login.

The examples I found on the web weren't much help. Specifically I want to test these two scenarios.

1

  • a) user signs in with valid credentials without having clicked "stay signed in"
  • b) user closes the browser, re-opens it and requests a protect page.
  • The user should not see the protected page.
  • The user should see the page asking them to signed in.

2

  • a) user signs in with valid credentials and having clicked "stay signed in"
  • b) user closes the browser, re-opens it and requests a protected page.
  • The user should not see the page asking them to sign in.
  • The user should be taken to the protected page.

My first attempt at solving the problem involved simulating a browser close by deleting the user_id I stored in the session (since it gets deleted on browser close). However, these tests failed because I was working in the request spec folder and have no access there to the session variables. My earlier related question: session available in some rspec files and not others. how come?

What is the best way to do these test with rspec?

Community
  • 1
  • 1
snowguy
  • 899
  • 2
  • 13
  • 23

3 Answers3

1

There are two problems here, that I think belong into different tests:

  1. User cannot access protected page when not logged in. That's a controller test.
  2. User gets logged in automatically even after the session has been destroyed, so long the "remember" me flag was set in the cookie.

For #1, you can try something like:

describe UsersController do
  context "when not logged in" do
    context "GET users/edit" do
      it "redirects to login" do
        get :edit, :id => 123
        response.should redirect_to login_path
      end
    end
  end
end

You can make a more general test case that asserts all actions which aren't explicitly listed, so that you don't have test gaps if the access code later becomes more permissive by accident. But that's a more subtle point.

For #2 you can write a request spec that sets the "remember me flag", then logs out, then logs in again and checks that you get to the page you expected. Drive all this from the browser by filling out credentials, checking the remember me box, clicking buttons.

The question is: Why? Why do you want to test this? Are you using a home-grown login system? Highly discouraged, unless you're a top-notch security expert. If you're not using a home-grown system, but instead Devise which comes tested, then don't re-test the library functionality. Just only test your application code, such as access rights to certain pages, which is covered by #1. You can also take a look at the tests that come with Devise how they test for this condition.

Hope this helps.

Update To clarify the request spec for #2. As mentioned in the other answer by @cutalion (who deserves the credit for the right answer), the mechanism for verifying that login can persist across session closing is built into the ActionDispatch IntegrationTest framework with open_session.

See Rails docs IntegrationTest API which includes examples. A blog post expanding on the use of a custom DSL.

Wolfram Arnold
  • 7,159
  • 5
  • 44
  • 64
  • I see how for item1, your #1 is an answer. Not following your sentence on #2. You say "you can write a request spec that sets the "remember me flag", then logs out, then logs in again and checks that you get to the page you expected." That wont' work for me. If I log out then the stay-logged-in cookie gets deleted because at that point the desired behavior is that when the user comes back they have to log in. AS noted you can see this behavior on Google. – snowguy Jul 06 '12 at 03:18
  • as to your last point...that I shouldn't be doing this yourself. Thanks for the advice. I'll take a look at devise. the authentication I wrote didn't seem difficult (except creating this automated test)--though I suppose it is possible I left a massive hole somewhere. I just followed pretty much what Michael Hartl did on his demo app (see http://ruby.railstutorial.org/ruby-on-rails-tutorial-book ). In his demo app it is like the user has always checked the "stay logged in" flag so I only needed to modify it to not stay logged in if the user didn't want to. – snowguy Jul 06 '12 at 03:23
  • Michael Hartl's tutorial is excellent in that he covers a lot of Rails very well and uses a TDD approach. I've used his tutorial as basis for teaching Rails courses myself. I think that the walkthrough an authentication system is great from an educational perspective, but I think it sets a dangerous precedent in making students believe they can write their own. I've discussed this with him personally. That said, it's a good tutorial. I **strongly** recommend Devise. – Wolfram Arnold Jul 06 '12 at 18:11
  • Regarding your first comment: You need to write procedural code, I'll try some pseudo code in an updated answer. Also, there are two types of cookies: The session cookie that dies when the browser tab is closed. That's used by Rails to store session info (by default) and can be accessed through the `session` variable in controllers. The other is a permanent cookie (it has a configurable expiration time like a year or so) that survives closing the browser tab and stores data you want to survive the session. That can be accessed using the `cookie` variable in controllers. – Wolfram Arnold Jul 06 '12 at 18:13
  • Wolfram, your comments are very helpful and I really appreciate the time you have taken to answer my questions. However, I believe there is some misunderstanding as to the desired behaviour of "stay signed in"--see my next comment. Breaking over two comments due to comment limitation size. – snowguy Jul 08 '12 at 05:22
  • stay signed in does not mean stay signed in if I log myself out manually. Instead it means stay signed in if I close my browser and come back to the site later. If you have a google account you can see this behaviour yourself. Click stay signed in when you log in. Then close your browser open it again and go to google. You'll see you are still signed in. But click "sign out" and go to any google page and you'll see that you are NOT signed in. Or am I misunderstanding your code? – snowguy Jul 08 '12 at 05:23
  • I see. I'm sorry for the misunderstanding. You'll need to use the session feature baked into the ActionDispatch IntegrationTest framework, as the other poster mentioned. I've not tried this recently, but the Rails API docs are pretty good and have examples. I'll update the post with a few links. – Wolfram Arnold Jul 08 '12 at 18:04
  • Thanks Wolfram for all your help. – snowguy Jul 09 '12 at 17:16
1

I think you should try standard rails method for integration tests - open_session.
Personally I never did that and can't give you tested code.

See multiple sessions example on rails guides.

cutalion
  • 4,334
  • 1
  • 38
  • 47
  • Thanks for the advice. I may end up resorting to that. My reason for not going there originally was just avoiding having too many different ways I deal with tests. – snowguy Jul 06 '12 at 03:15
0

This still seems to be a recurring problem in 2018. Some basic documentation is clearly missing. I finally found a solution: "Show me the cookies" gem.

Switching the Capybara driver clears the session, so one possibility would be changing the driver in the middle of a test to simulate browser close/open. But getting an alternative driver (for example selenium, or selenium_chrome) to work is not trivial, and besides this method would probably delete all cookies.

Also this simple command resets the session: Capybara.reset_sessions! But the problem again is that it not only destroys the session cookie but also permanent cookies. So it's useless for testing "Remember me" functionality.

I finally settled on Show me the cookies gem. It was very simple to install and implement. I just followed the provided directions for rspec. The command expire_cookies provides a satisfying simulation of quitting and opening a browser.

Jussi Hirvi
  • 565
  • 3
  • 6