We're hiring!

Stories

by Michel Martens on May 20, 2009

With Cucumber, you write stories and user acceptance tests in plain English, something a lot of people seem to like.

Feature: Search
  As a user
  I want to find information
  So I can learn more

  Scenario: Find what I'm looking for
    Given I am on the Google search page
    When I search for "rspec"
    Then I should see a link to RSpec-1.2.4: Home

Then you need to write your “step” files, where you add blocks with regular expressions that must match your plain English user stories and user acceptance tests. Inside those blocks you add your actual Ruby code. If you are using Cucumber for testing Rails or Sinatra, then your step definitions will look like this:

Given 'I am on the Google search page' do
  visit('http://www.google.com/')
end

When /I search for "(.*)"/ do |query|
  fill_in('q', :with => query)
  click_button 'Google Search'
  selenium.wait_for_page_to_load
end

Then /I should see a link to (.*)/ do |expected_url|
  click_link expected_url
end

Now that you have your plain English features files and your step files, you can finally run your tests. As a result, you will get a nice output of the user stories and user acceptance tests in plain English. But just to clarify that: the output is the same plain English stories you wrote at the beginning. The cost for this is to keep in sync two files for mostly the same thing.

In Citrusbyte we have a different workflow. We agree on the user stories with the client, and we put into instructions the user acceptance tests. We do that using Stories, which is a small add on for Contest. It allows you to declare user stories, scenarios and steps using Webrat. The same example, this time with Stories, would look like this:

class SearchStoriesTest < ActionController::IntegrationTest
  story "As a user I want to find information so I can learn more" do
    scenario "Find what I'm looking for" do

      visit "http://www.google.com/"

      fill_in "q", :with => "Citrusbyte Stories"

      click_button "Google Search"

      assert_contain "citrusbyte's stories"

      click_link "citrusbyte's stories at master - GitHub"

      assert_contain "Stories and User Acceptance Tests"
    end
  end
end

When you run that with the stories runner, you get this output:

As a user I want to find information so I can learn more
  — Find what I'm looking for
      Go to “http://www.google.com”
      Fill in “q” with “Citrusbyte Stories”
      Click “Google Search”
      I should see “citrusbyte's stories”
      Click “citrusbyte's stories at master - GitHub”
      I should see “Stories and User Acceptance Tests”

Now you can redirect that output to a text file and email it to your client, or you can use the stories-pdf runner, send the PDF straight to your client and get a delicious cake in return. Don’t believe me? Check this post.

You will have all your tests in one place, you will be writing Ruby, your clients will be happier than ever and the overhead will be minimal. Grab the gem from github and start impressing your clients today!

{ 1 trackback }

Forrest, un runner para stories con rails « Gastón Ramos – Ruby, Rails…
03.22.10 at 4:21 am

{ 12 comments… read them below or add one }

Michael 05.26.09 at 4:57 pm

The main benefit IMO of separating steps and user stories is reuse, with your approach in ‘Stories’ you won’t be able to reuse steps between different scenarios. As the cucumber features grow, I’ve found this to be particularly useful to avoid duplication.

Harry 05.26.09 at 9:59 pm

Nice! I usually end up ditching Cucumber and just writing my tests in plain webrat. For some extra sugar I’ve been using http://github.com/foca/storyteller until now, but I really like your textual formatting of the webrat actions. Good work!

Avdi 05.27.09 at 1:28 am

I wouldn’t consider this a Cucumber alternative. What you’ve got here is equivalent to doing WebRat acceptance tests in RSpec - something that some people do, and there’s nothing wrong with it. But it’s a different level of abstraction than Cucumber.

Michel Martens 05.29.09 at 8:45 am

@Michael: Code reuse is easy and we do it all the time. You only need to extract the repeated steps to a helper method, so instead of writing the same steps for, say, login, you just do login(@user) and you are done. You reuse code as you would in any Ruby program, by factoring out helper methods.

@Harry: Thanks a lot! I’m glad you like it. Storyteller’s author, foca, is a great friend of mine and we share the philosophy for small and efficient tools.

@Avdi: I think Cucumber gives you a false sense of abstraction, when in reality it’s just saying the same thing twice (in plain text and in code). I consider Stories a Cucumber alternative because it “serves as documentation, automated tests and development-aid”, which is Cucumber’s goal as stated in its website. And it does all that without requiring a huge library like Cucumber, that needs a Treetop parser and formatters because it wants to go from natural language to code, which in my opinion is not wise at all.

Anonymouse 05.29.09 at 3:44 pm

First, only a programmer could think Cucumber and Rspec code are similar. One is readable by mere mortals, the other raises more questions than it answers. (What’s “q”? That is an impementation detail that shouldn’t be at the top level.)

Scenario: Find what I'm looking for
Given I am on the Google search page
When I search for "rspec"
Then I should see a link to RSpec-1.2.4: Home

visit "http://www.google.com/"
fill_in "q", :with => "Citrusbyte Stories"
click_button "Google Search"
assert_contain "citrusbyte's stories"
click_link "citrusbyte's stories at master - GitHub"
assert_contain "Stories and User Acceptance Tests"

Second, the real world tends to be quite a bit more complex. The URLs are more complicated, the text is longer, there are more form fields, etc. In this case, you’d probably make a subroutine to hold the most of your assertions anyway.

Yes, Cucumber forces you to make every step a subroutine. But that gives you a chance to hide the implementation details, plus gives you much better naming of things.

Michel Martens 05.29.09 at 6:28 pm

@Anonymouse: Both Cucumber and Stories use Webrat for defining the steps, which is what you quoted below the plain English Cucumber features. No matter which library you use, you still need to write fill_in “q”, :with => “rspec” somewhere.

If you are using Cucumber, you will write:

  When I search for "rspec"

…in one file. Then, in another, you have to add this:

  When /I search for "(.*)"/ do |query|

…and finally this:

  fill_in('q', :with => query)

That is, in my opinion, three ways of saying the same thing. With Stories you only need to write that last part, because the text generation is automatic.

I understand your point, though: sometimes you want to hide the implementation details like “q”. We have a solution for that:

  report "When I search for 'Citrusbyte Stories'" do
    fill_in "q", :with => "Citrusbyte Stories"
    click_button "Google Search"
  end

Here is what report does: it silences the normal output and replaces it with the string you pass it. It’s very handy for the case you mentioned, but also when you want to group steps together.

You also said that Cucumber gives you much better naming of things, but you are free to write your own step definitions. To show this, I will cover your point about subroutines. Suppose you are testing Google and you do a lot of different searches. You would want to reuse the fill_in and click_button steps, so you write a search helper:

  def search(str)
    report "When I search for #{str}" do
      fill_in "q", :with => str
      click_button "Google Search"
    end
  end

Now each time you use search you get a better message in your output. And you had to write it only once, while with Cucumber you write it every time.

If you want to add messages for your own helpers or assertions, you can also use the report_for method. We have the following assertion in our test_helper:

  def assert_email_sent_to(recipient)
    assert_equal Array(recipient), ActionMailer::Base.deliveries.last.to
  end

Then, in order to have a nice message, we added this:

  module Stories::Webrat
    report_for :assert_email_sent_to do |email|
      "An e-mail should be sent to #{quote(email)}"
    end
  end

And the result is the same as if we were using any already defined assertion.

These are customizations that you will rarely do, but it’s good to have the ability to change the way you are presenting your user stories. Needless to say, when you need to change some text you do it in only one place.

All in all, the idea behind Stories is to make a more efficient use of your time by letting the program do the work for you.

Maxim Kulkin 06.05.09 at 2:08 am

Totally agree with post author. I always wondered who could spend so much time writing all those “When” and “Then” clauses just to use it in one story (or maybe two stories). IMO, it’s all bullshit that mere mortals can write somewhat complicated spec with Cucumber, because in fact he will need many different Whens and Thens.

So, I consider Cucumber just a tool for writing easy to read documentation (”stories”). And this tool does that perfectly. And also, it doesn’t distract developer with need to define another layer of abstraction. And did I mention that I love it being more lightweight than Cucumber ?

Michel Martens 06.05.09 at 9:16 am

@Maxim Kulkin: I agree with you and I’m glad you like this tool. Thanks for your comment!

Michael Heinrich 09.28.09 at 8:40 pm

Hello Michel,

As a Ruby newbie and a non Rails user, I would like to know if Stories (and Contest too) could be used with Watir to do test automation (Acceptance tests) .

Having looked over Cucumber, Shoulda, and Coulda, Stories and Contest seem to me to be more of the route I want to follow to do my testing.

Thank you!

Michael Heinrich 10.14.09 at 11:10 am

Hello Michel,

I have left previous comments but not sure why they are not showing up.

As I am a Ruby Nuby and new to the world of Unit/Acceptance Testing, I would like to ask how Stories (and Contest) work with Watir. My company does not have any Rails or Sinatra projects but does have websites using ASP.Net. I would like to use Stories as a test framework but am not familiar enough with Webrat. The websites do have a lot of AJAX and Javascript so I do not believe that Webrat would work.

Stories and Contest seem to be the best fit as they build on the Test::Unit::Testcase already available in Ruby and seem to be easier to follow than say Cucumber/Shoulda/Coulda.

Thank you in advance,

Michael

CRG 11.05.09 at 6:22 pm

This looks very nice. I’ve spent time with FitNesse and the Java and Ruby versions of Concordion. The former requires everything be put in tables. The latter two allow free form as well as tables, but require instrumenting HTML, a potentially tedious process. After using Ruby Concordion for a while, I went to plain ol’ Test::Unit and felt freer. I have not tried Cucumber, but I have heard from a couple of users that spec maintenance can be a bit of a pain.

The point with tools such as FitNesse, Cucumber, and Concordion is that they provide a mechanism for non-programmers to write tests. In the projects I’ve worked on, however, it seems the programmers end up writing the acceptances tests anyway. So perhaps for some teams, having a DSL is not all that important.

Any thoughts about how to approach a story that has several scenarios that differ only by a few values, while precluding a lot of duplication? FitNesse definitely and perhaps Concordion and Cucumber would use tables.

Michel Martens 12.11.09 at 11:47 am

@Michael Heinrich: I haven’t tried it with Watir, and I’m not familiar with it either, but I would say it should work. And if it doesn’t, an adaptation should be very straight forward.

@CRG: I’d say helpers are the way to go for removing duplication. You can define the helpers globaly or for each context, and in any case it let’s you abstract common patterns very quickly.

Leave a Comment

You can use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>