Persisting Logins and Sessions across scenarios


#1

I have an authentication filter on my application controller. I have the
following feature statements:

Scenario: I should be logged in to do any of this
Given we have a user named “myuser”
And the user named “myuser” logs in
When they visit the “entities” page
Then they should see the “entities” page

Scenario: Entity should have essential identification information

Given I do not have any entities
  And I am on the add a new entity page
When I enter "My Business Relation" in the "Common Name" field
  And I enter "My B.R. Legal Name" in the "Legal Name" field
  And I choose "Corporation" as the "Legal Form"
  And I press "Create"
Then I should save the entity information successfully

In scenario 1, the user is logged in successfully and the response body
after the ‘see the “entities” page’ is indeed the entities/index page.

When /should see the “(entities)” page/ do |resource|
response.body.should =~ /All Entities/m
end

When /on the add a new entity page/ do
visits new_entity_path
response.body.should =~ /Add a New Entity/m
end

However, in Scenario 2, I am assuming that a.) the same user (myuser)
and their associated login session is employed. Since the response body
from this is the login page then evidently this assumption is wrong and
something else is going on. Can some inform me as to how test logins
are managed/maintained/reused within cucumber/webrat?


#2

On Mon, Jan 12, 2009 at 10:45 AM, James B. removed_email_address@domain.invalid
wrote:

In scenario 1, the user is logged in successfully and the response body

However, in Scenario 2, I am assuming that a.) the same user (myuser)
and their associated login session is employed. Since the response body
from this is the login page then evidently this assumption is wrong and
something else is going on. Can some inform me as to how test logins
are managed/maintained/reused within cucumber/webrat?

Each scenario operates in a new session.

I usually have a step that aggregates the login process:

Given /^I am logged in as “(.*)”/ do |role|
#create a user whose name and role are based on the role
#log in that user
end

This lets me say:

Given I am logged in as “admin”
When I visit the super-secret page
Then I see it and learn about all its mystery

HTH,
David


#3

David C. wrote:

Each scenario operates in a new session.

I usually have a step that aggregates the login process:

Given /^I am logged in as “(.*)”/ do |role|
#create a user whose name and role are based on the role
#log in that user
end

This lets me say:

Given I am logged in as “admin”
When I visit the super-secret page
Then I see it and learn about all its mystery

HTH,
David

Thanks David, I have something like that:

When /user named “(.*)” logs in/ do |name|

assumes that the user given exists of course

visits root_path
UserSession.find.destroy if UserSession.find
Then “enter the username “#{name}””
Then “enter the password “#{name}-password””
Then “press the login button”
Then “welcome message”
end

But this seems needlessly expensive given that the entire application is
secured. Is there no way of preserving a login session for any
arbitrary period across both features and scenario? Is there a
technical or philosophical reason why this is not so?


#4

On Mon, Jan 12, 2009 at 6:21 PM, David C.
removed_email_address@domain.invalidwrote:

Then they should see the “entities” page
Then I should save the entity information successfully
response.body.should =~ /Add a New Entity/m

And coupling scenarios (or any kind of automated test - regardless of
framework) is a very bad idea.

Aslak


#5

On Mon, Jan 12, 2009 at 1:47 PM, James B. removed_email_address@domain.invalid
wrote:

At the start of every scenario, but is there no way of performing the
actual log in once, place the session info into an instance variable and
then use that rather than actually starting up a new session each and
every time?

Sure. You could mock out your session or deserialize a saved session
object in a Cucumber Before block on every feature except the one that
tests login. Or put the “Given” call that does the real session
initiation inside a Before so you don’t have to look at it every time.
If you want it to happen everywhere, you can also have a Before in
the env.rb file. You can’t chain the results from one scenario to
another, however, because the order scenarios run in (or whether they
run at all) is not guaranteed. So you’d have to put it in your setup
code.

On acceptance testing, I’d favor going through the actual session
creation logic instead of mocking it, even if you don’t put an
explicit “Given” every time. Yeah, it’s slower. But acceptance tests
aren’t supposed to be fast; they’re supposed to demonstrate real
usage, and be the last line of defense between your app and the big
bad world. They’re going to be slow with or without login each time.
And if there is some unseen routing or filtering bug in some
controller’s session access that doesn’t show up in your test because
you skipped past all that, and you lose a day and some hair trying to
figure it out, you’re going to wonder whether the seconds you saved
were worth it.


Have Fun,
Steve E. (removed_email_address@domain.invalid)
ESCAPE POD - The Science Fiction Podcast Magazine
http://www.escapepod.org


#6

Stephen E. wrote:

On acceptance testing, I’d favor going through the actual session
creation logic instead of mocking it, even if you don’t put an
explicit “Given” every time. Yeah, it’s slower. But acceptance tests
aren’t supposed to be fast; they’re supposed to demonstrate real
usage, and be the last line of defense between your app and the big
bad world. They’re going to be slow with or without login each time.
And if there is some unseen routing or filtering bug in some
controller’s session access that doesn’t show up in your test because
you skipped past all that, and you lose a day and some hair trying to
figure it out, you’re going to wonder whether the seconds you saved
were worth it.

Ok, Ok, Ok… It is no big deal to login in every time. If that is the
way it should be done in general then I have learned enough these past
weeks that I will go with the flow and save myself some headaches.

Thanks for the pointers and the explanations. I feel much more
comfortable about doing the login in each scenario now that I understand
the reasons.


#7

Aslak Hellesøy wrote:

On Mon, Jan 12, 2009 at 6:21 PM, David C.
removed_email_address@domain.invalidwrote:

Then they should see the “entities” page
Then I should save the entity information successfully
response.body.should =~ /Add a New Entity/m

And coupling scenarios (or any kind of automated test - regardless of
framework) is a very bad idea.

Aslak

I do not have a problem with putting :

Given I am logged in as “myuser”

At the start of every scenario, but is there no way of performing the
actual log in once, place the session info into an instance variable and
then use that rather than actually starting up a new session each and
every time?


#8

On Mon, Jan 12, 2009 at 11:49 AM, James B. removed_email_address@domain.invalid
wrote:

controller’s session access that doesn’t show up in your test because
the reasons.
Well you can inject it right into the session instead of doing the
full webrat thing. That’ll definitely cut down on the time it takes.

btw, I know it seems like you want this stuff to persist between
scenarios, but you really don’t. You want to ensure that the world is
in a known state before the test run, so that your tests are
repeatable and reliable. Getting it into a known state requires two
steps, basically

(1) get to a base state that works for EVERY SINGLE TEST. This
basically means an empty database, potentially populated with some
seed data (like if you store references to zip codes in the db)
(2) at the beginning of each test, modify the state to suit that test.
You should be changing only the state that you need to write an
expressive test, and no more

If you need to reuse logic, take advantage of one of the existing
reuse patterns. In cucumber’s case, that means extracting code to a
step and calling that in the scenarios. With RSpec, you’ve got a bit
more stuff like nested groups with a before block (which imo is NOT a
good idea for cucumber, nesting scenarios would be insane). What
you’re asking about is implicit coupling, and that’s not an effective
reuse pattern :slight_smile:

Pat