[rspec-users] Best practices for sharing state between story steps?
aslak.hellesoy at gmail.com
Wed Sep 10 03:34:04 EDT 2008
On Tue, Sep 9, 2008 at 11:31 PM, Jim Morris <ml at e4net.com> wrote:
> aslak hellesoy wrote:
>> 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.
> I agree and have run into this problem, but I have not found a better way in
> the environment I am using.
>> Cucumber starts a transaction when a scenario starts and rolls it back
>> when it ends (when on Rails).
> Nice if you are using Rails, which I am not :) I was curious if anyone had
> run into the same problems I do, because I don't use rails, I test directly
> against the HTTP API, so the database gets all this test data, you can't use
> transactions, so the database potentially has this state from previous
Sorry for not paying attention to this earlier.
> Right now in my Before_Scenario I generally try to clear the database, but
> that gets hard when it is relational with a ton of foreign key constraints,
> and you can't simply delete or truncate a table.
> As I said one work around is I create randomly named records every run, so
> as to avoid collision with previous data. I wonder if anyone has a better
> BTW my Web service is written in Java/Jetty/Spring so I have no Rails/Ruby
> niceties on that side, so it is nice to be able to use Ruby in the
> integration tests.
I've done that on several projects (using selenium/watir mostly). Some
things you could try:
1) HTTP database endpoint
A couple of years ago I was using Selenium against a Java web app. We
implemented a "special" HTTP endpoint for testing only that we would
access before a test. It would put the database in a pristine state.
It's clunky, but it works.
2) Ruby database endpoint
Another way to do it is to access the database directly from Ruby
before the test runs and clear it there.
3) Turn off constraints
Approaches 1 and 2 may be hard to do if you have complex constraints.
One strategy is to turn off constraints in your test database and just
do brute force deletes along with 1 or 2.
4) Incremental, semi-random data
This is the approach you are describing. The problem with this is risk
of false positives and intermittent failures. How risky it is depends
on your app of course.
5) Nested transactions
Using 1 or 2, start a transaction in the beginning of a scenario and
roll it back at the end. Only works if your database supports nested
I don't know if there is any good literature or discussion groups
about these topics, but there ought to be.
>>> 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.
> Ok I think I see the difference, by the state being in the Database or the
> response variable it does not implicitly couple steps like an @variable
> would. But it still requires the database to have been setup in a certain
> state by certain preceding Givens or Whens, which is a kind of coupling. But
> maybe I am stretching too far on that argument? I still feel coupling is
> coupling and state is state :)
>> In Cucumber a Before block has access to the same scope as the steps.
> Good that'll make the transition easier I hope.
> Jim Morris, http://blog.wolfman.com
> rspec-users mailing list
> rspec-users at rubyforge.org
More information about the rspec-users