9

In trying to test some sign-in/out functionality I want to delete some information from the session. I found out I couldn't access the sessions at all. I kept getting the error: undefined method `session' for nil:NilClass.

But then to my surprise I found out I could access session from other rspec pages. Additional details are below. My question is: Why can I access session from some files and not others? And how can I make it so that I can access session in my second example below?

Details File: spec/controllers/tenants_controller_spec.rb

require 'spec_helper'

describe TenantsController do
  specify { session[:tag].should == 'abc' }
end

File: spec/requests/test.rb

require 'spec_helper'

describe 'Test' do
  specify { session[:tag].should == 'abc' }
end

When I run the first file through rspec I get:

Failure/Error: specify { session[:tag].should == 'abc' }
  expected: "abc"
    got: nil (using ==)

Which is good. This should fail for that reason.

But, when I run the second file, I get:

 Failure/Error: specify { session[:tag].should == 'abc' }
 NoMethodError:
   undefined method `session' for nil:NilClass

So why is session an undefined method here?

snowguy
  • 899
  • 2
  • 13
  • 23

2 Answers2

12

Your 2nd test is a "request" spec which is an integration test. These are designed to simulate the browser and the helpers you get let you click buttons and fill out forms and assert text, tags on the page. It's the wrong level of abstraction for inspecting the session object.

If you want to stub out authentication, e.g. best to go through the app. See, e.g. here: Stubbing authentication in request spec

Know that integration tests are "exploratory" or "smoke" tests, high level tests that check the seams between components, not the guts of the components themselves. They're the most expensive to write and maintain. Use controller specs for verifying session stuff and move all business logic to the models where it's easiest to test.

Community
  • 1
  • 1
Wolfram Arnold
  • 7,159
  • 5
  • 44
  • 64
  • Thanks. This answers the question as I asked it but the answer unfortunately didn't get me where I wanted :( Anyway, I'll go ahead and ask another question about testing sign-in/sign-out stuff. – snowguy Jul 05 '12 at 20:55
  • 1
    Can you explain the problem you're trying to solve? There is a notion in the TDD community that if something is really hard to test, perhaps it's coded the wrong way too. If you can forumulate the test case before you have an implementation, the code tends to be looser coupled. Check the link I included, and post the link to the new question here, too. I'll see if I can help. – Wolfram Arnold Jul 05 '12 at 20:59
3

You are testing two different things. In the first test you test a Rails controller, so RSpec adds helpers like session for you. In the second test, you're in a request test, so RSpec does not add any controller related helpers.

UPDATE

I have not tested this, but looking at the source code I think this should suffice

include ::RSpec::Rails::ControllerExampleGroup

For more info check out the rspec-rails project on github.

UPDATE 2

You should not test for cookies in request specs, quoting the RSpec docs:

Request specs provide a thin wrapper around Rails' integration tests, and are designed to drive behavior through the full stack, including routing (provided by Rails) and without stubbing (that's up to you).

Draiken
  • 3,805
  • 2
  • 30
  • 48
  • So is there a way to get rails to load the necessary helper in the second case? How? – snowguy Jul 05 '12 at 16:41
  • The 2nd test is a "Request" spec--an integration test--which has different helpers available, it's not just plain Ruby code. There is a conceptual error here, and trying to shoehorn controller assertions and helpers into integration specs, makes it worse. See my answer. – Wolfram Arnold Jul 05 '12 at 19:04
  • @WolframArnold true, didn't see that it had the filename in it. Gonna update the answer – Draiken Jul 05 '12 at 19:09
  • I don't agree with the statement about request specs and cookies. It's an integration test that simulates interactions from the browser-side. If that's not the ideal place to check that a controller is properly handling cookie interactions with the browser, what would a better place be? – GuyPaddock Oct 08 '18 at 15:47
  • @GuyPaddock If you are testing on the integration level, cookies are not even a concept. You test that clicking here does X and I see Y. The *how* we do that, might be a cookie, might be a JS localstorage, etc. – Draiken Dec 13 '18 at 17:49
  • @Draiken I would argue that it depends on what you are testing integration _with_. The browser is something that you are integrating with. Cookies are something exchanged between the two components -- the system and the browser. – GuyPaddock Dec 20 '18 at 15:55
  • @GuyPaddock still an implementation detail. Not for a request spec. – Draiken Dec 21 '18 at 16:17
  • @Draiken So... what kind of tests would you recommend for verifying cookie handling? Rails itself uses integration tests for cookies. – GuyPaddock Dec 22 '18 at 00:14