Branching scenarios, GivenScenario and database

I’m trying to use stories to drive some high-level design.

I’ve got some branching scenarios where I want to follow a scenario,
to establish a base situation, and then have different scenarios which
‘branch’ out from that state, possibly several levels deep.

I asked a bit about this a few days ago, and David pointed out the
rather undocumented GivenScenario which seems to give the basic
ability to do what I’m after. So far it’s been working fairly well,
but I’ve run into a few quirks.

  1. I’ve been working through my scenarios, and I’d commented out the
    later ones to cut down on the ‘pending’ output while I develop the
    steps. I’d get one scenario working and then go on to the next. So
    at one point my plain text story looked something like this:

    Scenario A
    Given …
    When …
    Then …

#Scenario B

GivenScenario A

Given …

And I got Scenario A working and uncommented the next scenario, or so I
thought:
Scenario A
Given …
When …
Then …

#Scenario B
GivenScenario A
Given …

It took me a while to debug the resulting infinite loop. Perhaps
GivenScenario should check to see that it’s not asking for the CURRENT
scenario.

  1. Having figured that out and moving on, I now have another
    problem, which is what’s the best way to clean up the database so that
    Scenario A can run again if it assumes that the stuff it put in the DB
    isn’t there each time it runs. Cleaning the DB after running the
    scenario won’t work since it defeats the purpose of using the scenario
    as a pre-condition. Cleaning it before is more difficult,
    particularly if I’m using parameterized steps. It would be nice if I
    could somehow use database transactions, but I don’t see a nice way to
    do that either.

Has anyone faced issues like these? Any ideas?


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On Fri, Mar 14, 2008 at 8:12 PM, Rick DeNatale [email protected]
wrote:

but I’ve run into a few quirks.

#Scenario B
GivenScenario A
Given …

It took me a while to debug the resulting infinite loop. Perhaps
GivenScenario should check to see that it’s not asking for the CURRENT
scenario.

Patches welcome :slight_smile:

  1. Having figured that out and moving on, I now have another
    problem, which is what’s the best way to clean up the database so that
    Scenario A can run again if it assumes that the stuff it put in the DB
    isn’t there each time it runs. Cleaning the DB after running the
    scenario won’t work since it defeats the purpose of using the scenario
    as a pre-condition. Cleaning it before is more difficult,
    particularly if I’m using parameterized steps. It would be nice if I
    could somehow use database transactions, but I don’t see a nice way to
    do that either.

This should just work. If you look at story_adapter.rb, which defines
the RailsStory class (maybe we should align :slight_smile: ) you’ll see that the
transaction is opened when the scenario starts and rolled back when it
is finished. GivenScenario should not result in a call to any of the
scenario finished methods (scenario_succeeded, _pending, _failed), so
it should get rolled back.

If that’s not happening, please do file a ticket.

Cheers,
David

On 3/16/08, David C. [email protected] wrote:

On Fri, Mar 14, 2008 at 8:12 PM, Rick DeNatale [email protected] wrote:

I’m trying to use stories to drive some high-level design.

       When ...
       When ...

Patches welcome :slight_smile:

Fair enough, I’ll look at that when I find the time.

This should just work. If you look at story_adapter.rb, which defines
the RailsStory class (maybe we should align :slight_smile: ) you’ll see that the
transaction is opened when the scenario starts and rolled back when it
is finished. GivenScenario should not result in a call to any of the
scenario finished methods (scenario_succeeded, _pending, _failed), so
it should get rolled back.

If that’s not happening, please do file a ticket.

Okay, right now, I’m not actually using rails stories since I’m
working through pure business logic sans a UI so there’s been no need
for Rails “integration” testing.

On the other hand I guess it wouldn’t hurt to “declare” them as rails
stories.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On Sun, Mar 16, 2008 at 7:37 AM, Rick DeNatale [email protected]
wrote:

Okay, right now, I’m not actually using rails stories since I’m
working through pure business logic sans a UI so there’s been no need
for Rails “integration” testing.

On the other hand I guess it wouldn’t hurt to “declare” them as rails stories.

I assume that stories don’t get transactional goodness unless they’re
Rails stories.

Pat

On 3/17/08, Rick DeNatale [email protected] wrote:

[quite a lot]

Okay, I’ve got it working now. The problem seems to be wrapped up in
the use of transactional fixtures to load the db, and the fact that
fixture teardown actually happens in the SETUP phase, in other words
when you declare a fixture in Rails, it first deletes all records from
any tables with declared fixtures, and then loads the fixtures.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On 3/16/08, David C. [email protected] wrote:

On Fri, Mar 14, 2008 at 8:12 PM, Rick DeNatale [email protected] wrote:

This should just work. If you look at story_adapter.rb, which defines
the RailsStory class (maybe we should align :slight_smile: ) you’ll see that the
transaction is opened when the scenario starts and rolled back when it
is finished. GivenScenario should not result in a call to any of the
scenario finished methods (scenario_succeeded, _pending, _failed), so
it should get rolled back.

I’ve gotten stuck here.

To recap, when I don’t make the story a RailsStory, then the the first
scenario (A) works but fails when invoked from B as a GivenScenario.
One of the steps is failing because of a validates_uniqueness in on
of the models which fails because of stuff left in the DB from the
initial running.

When I change it to a RailsStory then Scenario A fails in the same
step the FIRST time. In this case it’s because records which it needs
aren’t there.

The records in question have been loaded by a helper which calls
Fixtures.create_fixtures to load them. These particular tables are
basically ‘constants’ for the app.

A few more tidbits.

There’s are models called Role, Membership, and a join model
MembershipRole

Role
has_many :membership_roles
has_many :memberships :through => :membership_roles
end

Membership
has_many :membership_roles
has_many :membersships, :through => :membership_roles
end

MembershipRole
belongs_to :membership
belongs_to :role
validates uniqueness_of :role_id, :scope => :membership_id
end

Role has a class method get which takes a string and effectivly does
this:

def self.get(name)
@role_objects ||= {}
@role_objects[name] ||= find_by_name(name)
end

And the method ultimately invoked by the failing step has a skeleton
like this:

class Membership < ActiveRecord::Base

def add_role(name)
if role = Role.get(name)

else
raise "Can’t find Role named #{name}
end
end

If I change the Role.get method to bypass the cache then the exception
gets raised the first time the step is executed for a RailsStory, and
second time for a non-RailsStory.

With that method left alone, the Role objects are found at some time
previous to the step and cached, in which case the failure happens
inside the if where a simplified version of that … looks like this:

  unless self.roles(true).include(role)
      membership_role = MembershipRole.create(:membership => self,

:role => role)
end
role.dependencies.each {|dependency| add_role(dependency)

The problem is that the validates_uniqueness_of in MemberRole is
failing, again the second time the step in question is called for a
non-RailsStory, and the first time for a RailsStory. The reason is
that the query generated by the roles association is doing a join on
roles and membership roles and the roles aren’t in the DB, but then
validates_uniqueness of just checks the membership_id and role_id
fields.

It looks to me as though running as a RailsStory is causing the roles
records to be deleted before the scenario runs, but after the class
cached them. s

Any ideas?


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/