[rspec-users] Best practices for sharing state between story steps?
aslak.hellesoy at gmail.com
Tue Sep 9 08:42:28 EDT 2008
On Tue, Sep 9, 2008 at 2:21 PM, Dan North <tastapod at gmail.com> wrote:
> Hi Jim.
> I guess I'm not a purist then - that looks fine to me, and it's probably
> something I would consider doing too.
> The thing to bear in mind is that there is magic going on when you run
> steps. Each step in a scenario is run in the context of the same object
> instance (which you don't get to see explicitly) by pixies, which means any
> @variables you set in one step should be visible to any other steps in the
> same scenario. I don't think of this as global state - it's simply a
> sensible way to share state across steps. You can think of the object the
> steps run in as a "world" (it might even still be called that - it was at
> one point) where you can set and verify state.
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.
Both are fine, but beware that as your codebase grows and you have
hundred or so step definitions, things can become *really* hard to
It won't bite you when you start on a new codebase, but in my
experience stateful steps will bite you later.
Having experienced this pain in one project, I decided to try out
stateless steps exclusively on the next project. I've found stateless
steps to be a tad more verbose than stateful ones, both in the text
and in the implementation (because you have to pass "identifiers"
around). Still, the improved maintainability of my features and
scenarios using this approach outweighs this slight increase in
Here is an example of a stateless scenario: (As a convention I'm
You can see the extra verbosity I'm talking about in the repetition of
"Aslak", "Beerfest" etc.
I'm still pragmatic about this of course, but now you know some of my
> There are bound to be pathological cases though, such as the before_scenario
> and after_scenario listeners. Perhaps there's a bug there, in that they
> should be running in the same object instance as the scenario steps
> themselves. Perhaps not. In the latter case I would definitely go with
> $globals to communicate state into your scenario, as long as you promise
> never to use them in your application code. Never, you hear me?
> 2008/9/8 Jim Morris <ml at e4net.com>
>> Sam Stokes wrote:
>>> Jonathan Linowes wrote:
>>>> I make some limited use of global (instance) variables that
>>>> correspond to english language pronouns. I have things like
>>>> @current_user (corresponds to "I"), @current_project (corresponds to
>>>> "the project"), etc. I am careful to be consistent. There's only a
>>>> handful of these, but I find it extremely convenient. Note, these
>>>> also tend correspond to 'states' in my app, which might be in the
>>>> session or part of a nested URL.
>>> So could I summarise this approach as, "use @variables, with good names
>>> and consistent principles for use"?
>> I'm of the opinion "do what works". I know there are purists that say
>> don't do this and don't do that, but when you come down to it you have to
>> use variables between steps, look at the rails examples and it sets a
>> response variable between steps.
>> What I do is horrible :) but "it works", I set GLOBAL variables (yup
>> $current_state), because I found that sometimes @variable didn't get setup
>> properly in some cases.
>> Then I use the listeners to clear those variables between scenarios, so
>> every scenario I have a listener (effectively before_scenario), that clears
>> all the global variables I use between steps, this avoids errors where I
>> don't set up something in a step but the scenario passes because it just
>> happens to have the right value in a global set by a previous test.
>> This does require some maintenance, however I could use a global hash for
>> my inter-step-dependencies, and just $hash.clear in the before_scenario
>> Ahh I remember why I had to use $variable and not @variable, for some
>> reason the before_scenario listener does not have access to the same scope
>> as the scenario so I have to use $variables.
>> So most of my scenarios look like this....
>> before_scenario do
>> $v1= nil
>> $v2= nil
>> Scenario "xxx" do
>> Given "something..."
>> When "I do something" # this will set $v1 and $v2
>> Then "check it was done" # this will check $v1 and $v2
>> I know purists will hate this ;) But "it works for me"
>> BTW I use stories entirely for integration testing, testing the entire
>> stack by using the same inputs (in my case a WEB based REST API) to the
>> stack as a client would.
>> I also do nasty things like delve into the database directly in a Then to
>> check the database actually got setup the way I expected by a previous When,
>> as well as checking the returned XML from the Rest-API call.
>> As always YMMV
>> Jim Morris, http://blog.wolfman.com
>> rspec-users mailing list
>> rspec-users at rubyforge.org
> rspec-users mailing list
> rspec-users at rubyforge.org
More information about the rspec-users