Behaviour Driven Design in Rails with Cucumber

Every week, the SaaS course will teach a new topic. This week, the topic is about Behaviour Driven Design (BDD). The main idea about BDD is to write the desired behaviour in plain english text first, then Cucumber will help to turn this plain english text into executable ruby code with regular expression matching (we’ll need to write the matching in regexp). By using BDD with Cucumber, we are actually designing our application from outside-in.

Developing in this way enables a developer and a (non-technical) customer to discuss the desired behaviour in the beginning and can prevent the developer to code something that is unwanted. I will walkthrough how to use Cucumber in the following of this post. I will skip the steps for installation, since it can be found elsewhere, so that I can focus more on explanation.


Introduction to the feature to be added

Let’s say we already have a site called Rotten Potatoes like below, and wanted to make the table title clickable for sorting purpose:

So we write our intended feature in a file call <filename>.feature where <filename> can be anything, examine the sort_movie.feature below, which I used for describing a clickable button for sorting in Cucumber:

Feature: display list of movies sorted by different criteria

As an avid moviegoer
So that I can quickly browse movies based on my preferences
I want to see movies sorted by movie title

Background: movies have been added to database

Given the following movies exist:
| title                                   | rating  | release_date |
| Aladdin                            | G          | 25-Nov-1992 |
| The Terminator              | R          | 26-Oct-1984 |
| When Harry Met Sally  | R          | 21-Jul-1989  |
| The Help                          | PG-13 | 10-Aug-2011 |
| Chocolat                           | PG-13 | 5-Jan-2001   |
| Amelie                              | R         | 25-Apr-2001 |
| 2001: A Space Odyssey  | G        | 6-Apr-1968    |
| The Incredibles               | PG      | 5-Nov-2004   |
| Raiders of the Lost Ark | PG      | 12-Jun-1981   |
| Chicken Run                    | G         | 21-Jun-2000  |

And I am on the RottenPotatoes home page

Scenario: sort movies alphabetically
When I follow “Movie Title”
Then I should see “2001: A Space Odyssey” before “Aladdin”


General Explanation of a .feature file

In the sort_movie.feature above, there are 3 parts, namely the feature, background and scenario. The feature portion describes about the feature that will be added, the text can be whatever without limitation. The background and scenario works almost the same except that the background will run first before the scenario. So we can think of background as giving some pre-condition to all scenarios in a feature. More information here: feature, background, scenario.

In a background or scenario, we can write multiple steps and the steps always revolve around Given-When-Then. (Note that: If I only mention scenario in later portion of the tutorial, it actually applies to background as well.) The steps will help to explain more specifically on what the scenarios is about. Note that the words underlined, such as: Given, When, Then is a special word in Cucumber, they are all used to describe stepsGiven describe the state of the application, When describe the event of something happening, and Then describe what is the consequences that will happen given an event happen at a particular state.


Explanation of sort_movie.feature

Feature:
The chunk of text under feature, is freestyle plain english text and it describes about our sorting feature from a point of view of a moviegoer.

Scenario:
I will introduce scenario before background, as it is more straight forward in this case:

Scenario: sort movies alphabetically
When I follow “Movie Title”
Then I should see “2001: A Space Odyssey” before “Aladdin”

The sentence after the colon in the first line is plain english. However, the When is a keyword, it is used to describe an event. We can now run ‘bundle exec cucumber‘ to execute cucumber. Now cucumber will complain this:

Undefined step: “I follow “Movie Title”” (Cucumber::Undefined)
features/sort_movie.feature:26:in `When I follow “Movie Title”‘

This is because we only write the intended behaviour, but we didn’t tell Cucumber what to do When I follow “Movie Title”. So now we need to add in a chunk of code in features/step_definitions/movie_steps.rb:

When /I followses “(.*)”/ do |sort_choice|
if sort_choice==”Movie Title”
click_on(“title_header”)
end

Note that this is actually ruby code. We write a regular expression to match our sentence in sort_movie.feature, so Cucumber will run this code when it encounters a matching sentence. The (.*) will find a matching word and place into the sort_choice variable. Then,  we check if the sort_choice is “Movie Title”, we can further enchance this code to sort by “Release Date” as well this way.

Note the click_on method. It is a method from Capybara. Capybara is a webkit that we’ll use to help us test our web applications by simulating how a real user would interact with your app. So it can help use simulate a user click on the Movie Title. The argument title_header can be the id, text, or value of the button that you want to click_on. More information about what Capybara can do: here.

Now we can run ‘bundle exec cucumber‘ again, but it will complain again, since we didn’t have a clickable button yet. So we need to add in our controller and view(not shown, just imagine the Movie Title in the top most picture became clickable).

The same goes to Then I should see “2001: A Space Odyssey” before “Aladdin”. We need to add a matching regular expression in movie_steps.rb:

Then /I should see “(.*)” before “(.*)”/ do |e1, e2|
aString = page.body.to_s
       if aString.index(e1)!= nil && aString.index(e2) !=nil
               if aString.index(e1) < aString.index(e2)
               else
               assert false,”jr_fail”
               end
       else
               assert false,”jr_fail”
       end
end

The code has nothing special except page.body.to_s will return us the html that is returned after we click_on the button. Then we use the result aString to check if the movies are arranged in order. More on page.body.

Background:

Given the following movies exist:
| title                                   | rating  | release_date |
| Aladdin                            | G          | 25-Nov-1992 |
| The Terminator              | R          | 26-Oct-1984 |
| When Harry Met Sally  | R          | 21-Jul-1989  |
| The Help                          | PG-13 | 10-Aug-2011 |
| Chocolat                           | PG-13 | 5-Jan-2001   |
| Amelie                              | R         | 25-Apr-2001 |
| 2001: A Space Odyssey  | G        | 6-Apr-1968    |
| The Incredibles               | PG      | 5-Nov-2004   |
| Raiders of the Lost Ark | PG      | 12-Jun-1981   |
| Chicken Run                    | G         | 21-Jun-2000  |

And I am on the RottenPotatoes home page

Note that the And is used after a Given, so it can be interpreted as a Given statement as well. Same as scenario, we need add in the steps:

Given /the following movies exist/ do |movies_table|
     movies_table.hashes.each do |movie|
            Movie.create movie
     end
end

For the And statement, we do no need to write the steps ourselves, since Cucumber-rails in features/step_definition/web_steps.rb already provide use with this:

Given /^(?:|I )am on (.+)$/ do |page_name|
visit path_to(page_name)
end

Note that path_to is defined here: features/support/paths.rb.


Now, if we run ‘bundle exec cucumber‘ again, we should have all pass.

You might wonder why is it so colorful in this tutorial, that is my attempt to differentiate the difference between Ruby, Capybara, Cucumber code. I might make mistake about the coloring, but you can always check like this:

print “movies_table: “, movies_table.class,”\n”
# result# movies_table: Cucumber::Ast::Table
or
print “page.class:”,page.class,”\n”
#result# page.class: Capybara::Session

By knowing where it belongs to, whether Capybara or Cucumber, you can then google the correct documentation.


So that’s it, we are developing from outside-in using BDD methodology and Cucumber tools. A little bit more to graduate from the Saas course that lasts for a month. TDD will be the next topic.

Thanks for reading!

Advertisements