[rspec-users] Step matchers

David Chelimsky dchelimsky at gmail.com
Sun Oct 14 23:30:18 EDT 2007


On 10/14/07, Pat Maddox <pergesu at gmail.com> wrote:
> On 10/14/07, David Chelimsky <dchelimsky at gmail.com> wrote:
> > On 10/14/07, David Chelimsky <dchelimsky at gmail.com> wrote:
> > > On 10/14/07, Pat Maddox <pergesu at gmail.com> wrote:
> > > > I think we all know that the readability of steps isn't great right
> > > > now, and in fact there's a very recent thread that discusses just
> > > > that.  It was that recent thread that prompted me to explore this a
> > > > bit.
> > > >
> > > > The basic idea is that you define step matchers, which have a regex,
> > > > and then you match step names against that regex.  Kind of tough for
> > > > me to explain so I'll just link to some code :)
> > > >
> > > > spec: http://pastie.caboo.se/107116
> > > > impl: http://pastie.caboo.se/107117
> > > >
> > > > Instead of writing
> > > > Given "a user named __ who is __ years old", "Pat Maddox", 22
> > > >
> > > > it allows you to write
> > > > Given "a user named Pat Maddox who is 22 years old"
> > > >
> > > > I wrote it out as a separate matcher because it was just easiest to do
> > > > it that way while I explored this approach, no messing around with
> > > > RSpec internals to get it to really work.  However if we went this
> > > > route the structure would certainly be different.
> > > >
> > > > Hopefully you can get the idea from the example code.  Ideally what I
> > > > would like is to write step libraries that are external to the stories
> > > > themselves.  The stories would be much clearer because the
> > > > implementation would not be embedded, and the step names themselves
> > > > would make a lot more sense.
> > > >
> > > > wdyt?
> > >
> > > LOL - I just suggested something like this in the other thread you
> > > cite. Though your idea strikes me as far more flexible and usable.
> > > Well done!!!!
> >
> > This just occurs to me. If we do away with the need for special
> > characters/positioning, blocks, etc, we should be able to do just
> > this, no?
> >
> > Given a savings account with 100 dollars
> > When the account owner asks for 101 dollars
> > Then the account owner should receive 0 dollars
> > Then the account should have 100 dollars
> >
> > Look mom - no quotes!
>
> How would you plan to implement this?  Read in a text file, strip the
> leading Given/When/Then and match each line?
>
> The one catch I see is that because stories are structured, you still
> need something to demarcate steps within a scenario and scenarios
> within a story.
>
> In general, I prefer your syntax, making it look like a plain-text
> spec instead of Ruby code.  I question whether it's a big enough win
> to justify writing a parser instead of simply writing valid Ruby.  At
> this point, personally I'd say no, but I can see how it would be nice
> for customers and spec readers/maintainers.
>
> Actually, would you mind showing an example of a full spec with this
> syntax?  Maybe something like:
>
> Story: Creating a post
>   As a user
>   I want post to my blog
>   So that I can share information with my fellow Rubyists
>
>   Scenario: anonymous user
>     Given no blog posts in the system
>
>     When I POST to /posts with title=Post Title, body=Post body
>
>     Then the page should show Post title
>     And the page should show Post body
>     And the page should show Posted by: 'anonymous'
>
>   Scenario: logged in user
>     Given no blog posts in the system
>     And a user named Pat
>     And logged in as Pat
>
>     When I POST to /posts with title=Custom Title, body=Custom body
>     And the page should show Custom Title
>     And the page should show Custom body
>     And the page should show Posted by: 'Pat'

At a high level, this is exactly what I came up with:

Story: employee accrues 1 day per month

  As an employee
  I want to accrue 1 day per month during my first year
  So that I can replenish my self

  Scenario: accrual after one month
    Given an employee
    When employee has worked 1 month
    Then employee should accrue 1 day vacation time

  Scenario: accrual after 2 months
    Given an employee
    When employee has worked 2 months
    Then employee should accrue 1 days vacation time

The Story: and Scenario: indicators are quite clear. This should be a
no-brainer to parse.

I think the real potential pitfalls in this approach have to do with
StepMatchers that have similar expressions and the user writes a step
with a one-word difference that invokes the wrong step and results in
inaccurate feedback.

That said, the ability for customers to be able to write
stories/scenarios in plain text is the holy grail IMO. That is the
promise of FIT. I've seen failure and success with FitNesse. The
success required customers who were able to grok tables and wikis. It
fell down when they didn't care about these things, or when they
didn't understand how to structure things w/ correct setups,
teardowns, etc.

A lot remains to be seen, but I think this plain text approach will
eliminate a LOT of that. Especially situations where deveoper-centric
things like setup became part of the customer facing FitNesse pages.
With the Story Runner, we'd be able to hide all of that out of the
view of the customer. Great stuff!

>
> Actually a parser for this would be quite simple

Dead simple. It would also allow us to do away with methods like
Given, When and Then, which some people have objected to (because of
the capitalization), because the stories are no longer expressed
directly in Ruby. Internally, the parser could use a StepFactory to do
things like create_given, create_when, etc (or however we decide to
name these).

I'm really excited about this idea!

Cheers,
David

> and I definitely
> prefer it to the Ruby version.  It makes sense to use the interpreter
> when you're defining step implementations inline, but if you're not
> then it doesn't really add any value and looks/feels weird.
>
> One other thing I thought of is that we can combine spec matchers with
> your ? syntax idea.  Basically, regexps don't really get you anything
> in this type of situation, since you're going to be matching the whole
> string, and regexp doesn't convert data types for you.  You could
> define a step like
>
> Step "I like to eat, eat, eat ? and ?" do |food1, food2|
>   @fav_foods ||= [food1, food2]
> end
>
> and then when it gets used in the spec, it's simply
> Given I like to eat, eat, eat apples and bananas
>
> Under the hood, the ? get converted to (.*), which again works fine
> since you're matching the entire string exactly anyway.  It's a lot
> more readable than full-blown regexps.
>
> Pat
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>


More information about the rspec-users mailing list