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

_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to