[rspec-users] Best practices for sharing state between story steps?
matt at mattwynne.net
Tue Sep 9 15:52:45 EDT 2008
On 9 Sep 2008, at 19:52, Jim Morris 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.
> (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.
I think the scenarios are each wrapped in a transaction, so (as long
as you're using the right type of database / tables) the slate should
be wiped clean between each one.
>> 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.
> 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.
I've been thinking about this a bit, after reading Aslak's advice
which went contrary to my instincts. It seems like you could probably
do something like this instead:
Scenario: testing a pet
Given I have a pet
When I stroke the pet
Then the pet's health goes up
Because your Given step creates just one Pet instance, you can safely
refer to it as "the pet" in the other steps, and simply call Pet.find
(:first) in the step matcher.
In a more complicated scenario:
Scenario: testing a pet
Given I have two pets named "my dog" and "my cat"
When I stroke "my dog"
Then "my cat" should be pissed off
Now your step matcher needs to refer to a particular pet, but can't
it just use the name as a key, since that will be unique within the
set of two Pet records you created in your Given step?
When /I stroke "(.*)"/ |pet_name| do
Does that make sense to you?
In case you wondered: The opinions expressed in this email are my own
and do not necessarily reflect the views of any former, current or
future employers of mine.
More information about the rspec-users