[rspec-users] Best practices for sharing state between story steps?

aslak hellesoy aslak.hellesoy at gmail.com
Tue Sep 9 16:21:41 EDT 2008


On Tue, Sep 9, 2008 at 8:52 PM, Jim Morris <ml at e4net.com> wrote:
> aslak hellesoy wrote:
>
>> The debate seems to be whether step definitions should be stateful or not.
>> In practice this is achieved by setting one or more @variables in a
>> step and reusing them in a different step - all within a scenario.
>>
>
> I think that is the debate, but I'd like to point out that there is always
> state between steps, it is just a matter of where it is being kept. In your
> example for instance most of the state is being kept in the database between
> steps (ie between Given an "active" site_user names "aslak" and he following
> steps.
>

There is persistent state (database) and object state (the object that
serves as a context for a scenario).

I'm not saying that state in and of itself is bad. However, *coupling*
is bad - for maintenance reasons. If the steps share object state (in
@variables) they become coupled.

So why is coupling bad for maintenance? When your step library grows
to several dozens or like in my case, over a hundred, you want to be
able to cherry-pick steps to build new scenarios. If you have coupled
them with object state, you can't do that. These steps will simply not
work unless they're used alongside the steps they are @coupled to.

That's when you start to add new step definitions with a similar
semantic and wording, but with a different implementation - to work
around the other steps that you realised you can't reuse. Now things
get confusing. You have Given /there is a contract named (.*)/ and a
Given /there is an existing contract named (.*)/ etc. etc.

> (As a side question how do you clean up the database between tests, so the
> "state" from the previous Scenario doesn't affect the next Scenario?) In
> some cases I use a randomly generated record each time.
>

Cucumber starts a transaction when a scenario starts and rolls it back
when it ends (when on Rails).

>>
>> Here is an example of a stateless scenario: (As a convention I'm
>> "quoting" variables)
>>
>
> That is quite interesting, however as I pointed out above there is state
> between steps, in the database and in the response.
>

That's not the coupling kind of state I'm talking about. That's ok.

> I actually do something very similar, with named resources in each step,
> however I find I still need to use variables between steps, here is an
> example... (Not rails)
>
> Scenario: testing a pet
>  Given I have a pet named "my dog"
>  When I stroke "my dog"
>  Then "my dog"'s health goes up
>
> In the Given I create a pet named "my dog" in the database either directly
> or through the REST-ful API to my Web app, however in both cases I need the
> returned id of the created pet to make any further calls on it.
>
> In the When clause I need to make an API call that pets "my dog", the API is
> POST stroke/345
> So what I do is in the Given I assign the returned id of the new record to
> $petid, then in the When clause I do something like,
> $http_req.post("stroke", id => $petid).
>
> Now I needed to carry that variable $petid around otherwise how would I be
> able to make the call in When.
>
> Similarly when I want to check my pets health in the Then clause I still
> need that $petid, whether I check the value directly in the database or make
> an API call that returns my pets health.
>
> I don't see anyway around keeping state in these cases.
>
> Now I do clear that state ($petid= nil) in the before_scenario hook, so
> another scenario which may be written badly and not assign the $petid, won't
> succeeed due to a previously successful scenario.
>
> I'd definitely be interested in better ways to do this though, as I hate
> passing global variables around (as I said in an earlier post I can't use
> @variable because the before_scenario does not seem to have access to the
> same scope as the scenario that is about to run). Although I think someone
> said each scenario has its own variables? I didn't notice that behavior.
>

In Cucumber a Before block has access to the same scope as the steps.

>
> --
> Jim Morris, http://blog.wolfman.com
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>


More information about the rspec-users mailing list