Multi-line steps

Hi

I just wondered what the plan was for multi-line steps. Is anyone
working on it? I’ve just come across a real use for it (including
small text files in the story).

Cheers
Ashley


http://www.patchspace.co.uk/

Hi Ashley,

What’s your use case? I’m curious…

Tim.

On Wed, Apr 9, 2008 at 12:31 PM, Tim H. [email protected] wrote:

Hi Ashley,

What’s your use case? I’m curious…

Tim.

I’m working on a Treetop (http://treetop.rubyforge.org/)
implementation of the Story parser.
Please file any suggestions for improvements to the story format to
lighthouse and tag with story_grammar

Aslak

On Apr 9, 2008, at 8:13 AM, aslak hellesoy wrote:

I’m working on a Treetop (http://treetop.rubyforge.org/)
implementation of the Story parser.

Hey Aslak, while I love the idea of exploiting treetop for this, this
would be our first external dependency for end users. That was one of
the reasons I didn’t use treetop in the first place - it had just been
released and Brian suggested exporing it when I was working on Plain
Text Stories.

Personally, I’m in support of external dependencies, especially in
light of recent improvements to Rubygems that make it even easier for
a gem to manage its own dependencies. But thus far we’ve had an only
slightly-less-than-official policy of no external dependencies. Has
your thinking on this issue shifted as well?

On Wed, Apr 9, 2008 at 6:03 AM, Ashley M.
[email protected] wrote:

Hi

I just wondered what the plan was for multi-line steps. Is anyone
working on it? I’ve just come across a real use for it (including
small text files in the story).

It’ might be a little clunky, but I’ve recently done something like
this in a story involving uploading a csv file


When Fred creates a csv file
And the csv file has a record with “first_name”, “last_name”
And the csv file has a record with “Bilbo”, “Baggins”
And the csv file has a record with “Arwen”, “Evenstar”
And Fred uploads the csv file

The step for the first line creates an empty string object, and the
next three append to it. The final step does whatever is needed to
turn the string into a TestUploadFile or whatever is needed.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On Wed, Apr 9, 2008 at 2:41 PM, David C. [email protected]
wrote:

Personally, I’m in support of external dependencies, especially in
light of recent improvements to Rubygems that make it even easier for
a gem to manage its own dependencies. But thus far we’ve had an only
slightly-less-than-official policy of no external dependencies. Has
your thinking on this issue shifted as well?

I realize it will introduce a dependency on treetop, but now that
rubygems are ubiquotous and has largely improved with 1.1.0 I think
it’s ok. It would only be needed for stories.

A treetop based parser has several benefits:

  • Much simpler codebase
  • Much better error messages for users. Line numbers and conflict
    detection.
  • Easier integration with other tools (including rspec core)
  • Simpler setup for story running (I have a vague plan for that in my
    head).

I haven’t committed the code yet - for now it will live in a separate
repo on GitHub. Here is a teaser for the grammar:

grammar Story
rule story
header narrative scenario*
end

rule header
'Story: ’ sentence_line
end

rule narrative
‘As a’ sentence_line
end

rule scenario
'Scenario: ’ sentence_line step*
end

rule step
# The various step rules are generated dynamically
'Given ’ (step_1 / step_2)
end

Dynamically generated rule

rule step_1
'I am ’ word ’ and ’ word
end

Dynamically generated rule

rule step_2
'I was ’ word ’ and ’ word
end

rule word
([\w])*
end

rule sentence_line
(!eol .)* eol
end

rule eol
“\n” / eof
end

rule eof
!.
end
end

Basically, each run will be based on a core grammar which is extended
by the user, creating extra rules for each parameterised step.
The treetop based parser would create the grammar dynamically, compile
it in-memory and use it to parse plain text stories. My first
benchmarks are pretty good - 0.03 secs to parse/compile the grammar
and less than 0.0001 secs to parse a simple story.

This is fun!

Aslak

On Wed, Apr 9, 2008 at 9:14 AM, aslak hellesoy
[email protected] wrote:

I’m also interested in how we can do more FIT-like things using
tables. But maybe that belongs in a different tool…

Like, uh … FIT?

I’ve been thinking of resurrecting the Ruby FIT project which seems to
have no commits since 2006. I can definitely see an RSpec-FIT bridge
(separate library) that allows you to run FIT pages and RSpec
scenarios with unified reporting.

Of course, this will have to wait for RSpec 1.2, the book, and maybe a
vacation or two …

Cheers,
David

On Wed, Apr 9, 2008 at 3:33 PM, David C. [email protected]
wrote:

On Wed, Apr 9, 2008 at 9:14 AM, aslak hellesoy [email protected] wrote:

I’m also interested in how we can do more FIT-like things using
tables. But maybe that belongs in a different tool…

Like, uh … FIT?

Exactly. I have tried RubyFIT - it’s OK, but dead as you mentioned. It
would be nice with a more recent RubyFIT implementation that leverages
all the goodies in the RSpec code (and community).

Aslak

On Wed, Apr 9, 2008 at 2:59 PM, Rick DeNatale [email protected]
wrote:

It’ might be a little clunky, but I’ve recently done something like
this in a story involving uploading a csv file


When Fred creates a csv file
And the csv file has a record with “first_name”, “last_name”
And the csv file has a record with “Bilbo”, “Baggins”
And the csv file has a record with “Arwen”, “Evenstar”
And Fred uploads the csv file

Just a style comment: I usually strive for a single When (in this case
Fred uploads the csv file).
The other ones are Givens.

The step for the first line creates an empty string object, and the
next three append to it. The final step does whatever is needed to
turn the string into a TestUploadFile or whatever is needed.

Still interested in seeing how a multiline step would look.
I’m also interested in how we can do more FIT-like things using
tables. But maybe that belongs in a different tool…

Aslak

On Wed, Apr 9, 2008 at 9:14 AM, aslak hellesoy
[email protected] wrote:

Just a style comment: I usually strive for a single When (in this case

Fred uploads the csv file).
The other ones are Givens.

Yes, that’s how I actually wrote it, just checked. I was working from
memory from a machine which didn’t have the actual code, and with too
little coffee in my system for that hour of the morning to boot!


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On 9 Apr 2008, at 14:14, aslak hellesoy wrote:

Just a style comment: I usually strive for a single When (in this case
Fred uploads the csv file).
The other ones are Givens.

Hmm, I’ve written a lot of Selenium stories lately, and they look like
this:

Given …

When the user visits /my_page
And clicks on “Edit”
And types “Cow” in the favourite_animal text field
And clicks the save button

Then …

Is that bad?

Ashley


http://www.patchspace.co.uk/

On 9 Apr 2008, at 11:31, Tim H. wrote:

Hi Ashley,

What’s your use case? I’m curious…

Tim.

Hi Tim

I’ve finally started writing a tool I’ve wanted for ages: a REAL
database migration system, that works on a patch system with
dependency tracking (like darcs), not a borked^H^H^H^H^H^H linear
scheme like AR Migrations. So I started writing the story and I
wanted to write something this:

Scenario: two steps depend on third
Given a file “db/migrate/a.migrate.rb”:
migration “migration A” do |m|
m.depends_on “migration X”
end
And a file “db/migrate/b.migrate.rb”:
migration “migration B” do
m.depends_on “migration X”
end
And a file “db/migrate/x.migrate.rb”:
migration “migration X” do
end

 When ...

In the end I did it like the way Rick’s mate Fred builds CSVs, but
breaking down a single step can be cumbersome.

Ashley


http://www.patchspace.co.uk/

On 4/9/08, Ashley M. [email protected] wrote:

Given …

When the user visits /my_page
And clicks on “Edit”
And types “Cow” in the favourite_animal text field
And clicks the save button

Then …

Is that bad?

Not bad, but perhaps misleading. Given is used to express
preconditions, wheras When is for an action. Her you are expressing
preconditions, even if those preconditions are action-based instead of
state-based (e.g. the known state of the work is that the user has
performed these actions)

Does that make any sense?

Pat

On 9 Apr 2008, at 22:04, Pat M. wrote:

Not bad, but perhaps misleading. Given is used to express
preconditions, wheras When is for an action. Her you are expressing
preconditions, even if those preconditions are action-based instead of
state-based (e.g. the known state of the work is that the user has
performed these actions)

Does that make any sense?

I knew you would say that :slight_smile:

It just seems unclear, I mean you could turn

When the user vists /my_page
And clicks the do_something button

into either

Given the user is on /my_page
When he clicks the do_something button

or

When the user clicks the do_something button on /my_page

The first solution looks better, but I guess it depends how atomic you
want to make the steps. You could always write this, if you were so
inclined…

Given the user has a browser
And they have typed “http://www.mysite.com” into the address bar
And pressed enter
And moved the mouse over the do_something button
And pressed the mouse button

Wouldn’t wanna write that as one step…

Ashley


http://www.patchspace.co.uk/

On 10 Apr 2008, at 18:28, Glenn F. wrote:

Given database is in this state
When user does this stuff in browser
Then database should be in this new state

I like the way you phrased this Glenn. Maybe the distinction is that
Given is everything outside the control of the user, or, if the user
set up the Given themself, all the stuff they completed in a
previous session (no transition states). What erked me about Pat’s
earlier suggestion was it felt like half the stuff the user did was in
Given, which left the end of the Given section describing a state that
a user could not find the system in.

I don’t see that Given or Then has to be restricted to the database
though - it could refer to the pre- or post-When state of an external
web services, the contents in the browser, files read or written.
Anything really.

Ashley


http://www.patchspace.co.uk/

On Thu, Apr 10, 2008 at 7:28 PM, Glenn F. [email protected] wrote:

The other ones are Givens.
And clicks the save button

When user does this stuff in browser
Then database should be in this new state

This is a very “technical” way to express a scenario. It wouldn’t make
much sense to a typical domain expert (unless they know about
databases).
I generally try to write scenarios using the domain language,
focussing on outcomes for users (not computers). Example:

Given the customers joe, paul and lisa are registered users
When a user signs up as lisa
Then the user should be informed that the name is taken
And the user lisa should not be able to log in

Aslak

On Apr 9, 2008, at 5:04 PM, Pat M. wrote:

like

Pat
This actually sounds more confusing to me when viewed in the context
of my own stories, and it seems similar to what’s going on here. I
write a lot about the user’s interaction with the site and what should
happen, so I have a lot of stories that look like:

Given database is in this state
When user does this stuff in browser
Then database should be in this new state

If the point of the Story is to see what happens when a user clicks
buttons while the app is in a certain state, then moving those to a
“Given” means you have no more actions left. If all the action
happened in the “Given”, then there won’t be a When statement at all.

Glenn

On Thu, Apr 10, 2008 at 5:25 PM, aslak hellesoy
[email protected] wrote:

And the user lisa should not be able to log in
I’m not sure which of the two lisas I should feel sorry for.

Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Aslak

You’re right, and I don’t literally write them in this format. But if
you interpret the business logic that’s in them into what they
actually mean technically, it really just is the state of the database
and I guess we can also add the session/cookies/flash.

Even in the example you just gave, you express in your “Given” that in
the Users table of your db there are 3 entries. For your “When” there
is a user interacting with the web app. “Then” shows that an error is
in the response.

You could also check to ensure that the number of Users in the
database did not change. This, I know, is more of a technical way to
look at it, but I’ve personally found use for this when realizing a
tricky view was passing bad data to a controller and my Story caught
it. It was getting the right flash message in the end… but there
were too many entities being created in the db. I didn’t catch this
until the Story spec so I still think there’s a use for this!

You could even break apart “And the user…” into:

When a user tries to log in as lisa
Then the user should be informed that no such customer exists

Because really the current statement includes multiple steps. Going
to the login page, filling out the data, submitting it, and then
checking the response/redirect. If there were an error in your “Then
the user $lisa should not be able to log in” step, it would be
untested and it’s actually not quite trivial since it’s not a single
step. That’s probably more nit-picky since I’m sure the step would be
used in many other places to give you added confidence, but since you
mentioned “When” is for actions and your example “And Then” has an
action, I still think it’s interesting to look at :slight_smile:

Glenn

On 11 Apr 2008, at 03:03, Rick DeNatale wrote:

And I wouldn’t think that most reasonable systems would disable
Lisa1’s account because some Lisa2 tried and failed to use the same
name.

Somebody, somewhere has built a system that does this. You know it.


http://www.patchspace.co.uk/

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs