[rspec-users] Cucumber - Adding a step definition

Matt Wynne matt at mattwynne.net
Sun Mar 15 14:37:01 EDT 2009

On 15 Mar 2009, at 16:52, Ben Mabey wrote:

>> I know what you mean, and it would certainly make for nice readable  
>> scenarios. My worry is how maintainable the steps would be in the  
>> long run. I'm used to seeing steps like this:
>> Given /the Policy has a secondary risk/ do
>>  Policy.count.should == 1
>>  policy = Policy.first
>>  policy.secondary_risk = true
>>  policy.save
>> end
>> Which are pretty explicit and leave no room for accidental abuse.  
>> What the OP suggested would give you something more like this:
>> Given /a Policy/ do
>>  @it = Factory(:policy)
>> end
>> With /a secondary risk/ do
>>  @it.secondary_risk = true
>>  @it.save
>> end
>> I dunno actually. Now I type it out it doesn't seem so bad :)
> As previously mentioned in the thread another way to phrase this is  
> with step tables.  Here is the code I use:
> http://gist.github.com/59007 (related blog post: http://www.benmabey.com/2009/02/05/leveraging-test-data-builders-in-cucumber-steps/)
> I have been using instance variables in my steps (but naming them  
> after the model, i.e. @policy) but Matt's DB solution is nice in  
> some respects.  Both ways fall apart when you have multiple polices,  
> but in that case you should probably be passing in the name or some  
> other identifying aspect of the model.  Anyways, it seems like a  
> pattern is emerging on how to carry state to other steps when you  
> have to.  I was bored so I combined both approaches:
> module ObjectLocators
> def the_policy
>   if @policy
>     @policy
>   else
>     case Policy.count
>     when 0
>       raise "There is no @policy variable defined and no policy in  
> the DB! Establish state in previous step or create a new policy."
>     when 1
>       Policy.first
>     else
>       raise "There are multiple policies in DB and no @policy  
> variable set!  Please disambiguate the state in previous step."
>     end
>   end
> end
> end
> World { |world| world.extend(ObjectLocators) }
> (this code is here: http://gist.github.com/79470)
> Like I said, I was bored and haven't really tried this.  I don't  
> know if I even like it, but I thought extracting out the patterns  
> would be useful.  You could leverage factories just as I do in my  
> previous gist to provide locator (locater?) for all of your models.

We have some very similar code deep in the bowels of Songkick's  
features folder that is a little scrappy, but does help us solve this  
problem quite nicely.

Basically there's a regular expression that matches on phrases of the  
following form:

   the Widget
   the Widget "Foo"

So you can use that in your step matchers like this:

   When /I delete (#{THE_THING})/ |identifier|
     thing_to_delete = identified_model(identifier)

If you want to remember something just by the "Foo" label, you have to  
throw it into a collection, imaginatively named 'stuff', as you do so:

   stuff["Foo"] = Factory(:widget, :name => "Foo")

As I say it's a little scrappy as it's something we threw together  
ages ago and haven't really felt the need to revisit, but hopefully it  
will give someone some ideas:

Matt Wynne

More information about the rspec-users mailing list