[rspec-users] [cucumber] accessing session variables

Matt Wynne matt at mattwynne.net
Thu Mar 12 04:37:55 EDT 2009


On 11 Mar 2009, at 18:52, Balint Erdi wrote:

> Hey,
>
> I have the following simple scenario and the step definitions:
>
> Scenario: Create album
>  Given I am logged in as "pepito"
>  When I go to my profile page
>  ...
>
> Given /^the user (.*) exists$/ do |login_name|
>  User.find_by_login(login_name) || Factory(:user_with_password, :login
> => login_name)
> end
>
> Given /^I log in as (.*)$/ do |login_name|
>  user = User.find_by_login(login_name)
>  # it is supposed that the user was generated by the
> :user_with_password fixture
>  # that has the 'secret' password
>  post "/session", :login => user.login, :password => 'secret'
> end
>
> Given /^I am logged in as "(.*)"$/ do |login_name|
>  Given "the user #{login_name} exists"
>  Given "I log in as #{login_name}"
> end
>
> When /^I go to (.+)$/ do |page_name|
>  visit path_to(page_name)
> end
>
> def path_to(page_name)
>  case page_name
>
>  when /my profile page/i
>    member_profile_path(:id => session[:user_id])
>  ...
> end
>
> I receive the following error:
>
> When I go to my profile page  #
> features/step_definitions/webrat_steps.rb:6
>  You have a nil object when you didn't expect it!
>  The error occurred while evaluating nil.session (NoMethodError)
>     /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/ 
> action_controller/test_process.rb:429:in
> `session'
>
> So it seems like the session is not accessible -or not this way- in  
> the
> step definitions. I finally came up with that workaround:
>
> def path_to(page_name)
>  case page_name
>
>  when /my profile page/i
>    member_profile_path(:id => User.first)
>  ...
> end
>
> However, I am not at all content with this hack. It does not go to the
> profile page of the logged in user but to the first one. This can be  
> the
> same -and in this scenario it is- but there is no guarantee for that.

IMO, trying to access implementation details like sessions from your  
acceptance tests is an anti-pattern, to be avoided.

The puzzle you've hit here is that there is state implied in your  
scenarios by the use of the phrase "*my* profile page". I think there  
are two approaches this:

(1) You remove the implicit state from the scenario by re-writing your  
step as "When I go to the profile page for the User". This means that  
you can safely now use User.first, and you can even assert at the top  
of that step that User.count.should == 1, because the step refers to  
"the User" - so there must only be one, right?

(2) You model the state implicit in the scenario within your tests by  
writing an instance variable on the Scenario's world when you log in -  
something to represent "me".

There is a third approach where you write a special 'API' on your app  
which the tests can use to find out who "me" is. That might be as  
simple as conventionally putting it in a HTML comment on the page, or  
even displaying it on every page, or as over-engineered as a REST GET  
request to a user/current or /session resource.

However you chose to do it, I would urge you to avoid going under the  
covers to grab the session object directly - it's a path to hideous  
complexity IMO. Better to keep your test layer as separate as you can  
from the code.

Does that make sense?


Matt Wynne
http://blog.mattwynne.net
http://www.songkick.com



More information about the rspec-users mailing list