CSC/ECE 517 Fall 2012/ch2a 2w26 aj: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
(24 intermediate revisions by the same user not shown)
Line 2: Line 2:


== Introduction ==
== Introduction ==
The article is intended to be an accompaniment to the SaaS video lecture 4.6  titled “Enhancing Rotten Potatoes again” <ref name="video">Enhancing Rotten Potatoes again, http://www.youtube.com/watch?v=2l9uLw3y6J8<br></ref>, part of the SaaS video lecture series for the Software Engineering for SaaS hosted on the Coursera learning network. <ref>Coursera, https://www.coursera.org/</ref><br>The Rotten Potatoes webpage, designed in the previous lectures is enhanced for implementing a new feature to add a new movie by looking up the  [http://www.themoviedb.org/ TMDb database] rather than manually entering the movie name. The flow of this article is as follows :


*The scope of discussion
*Brief description of the storyboard used for the Lo-Fi UI design discussed in previous lectures in the video lecture series,
*Explanation of Sad path, Happy Path and Bad Path
*Description of the User Story developed in the video lecture
*Elaboration of the test scenarios written for the particular User story (including scenarios for happy and sad paths) in Cucumber
*Explanation of the Background facility in Cucumber


===Code Blocks===
<b>Note : </b>All images used in this article have been taken from screen captures of Coursera video lectures available [https://class.coursera.org/saas-2012-003/class/index here]. All content is owned by original authors and there is no copyright infringement intended. References have been provided in the image descriptions wherever necessary


==Closures in Ruby==
==Scope==
 
The scope of this article is limited only to the scope covered in the video lecture. The lecture builds upon [http://en.wikipedia.org/wiki/Behavior-driven_development Behavior-Driven Development (BDD)] basics discussed in the video lectures before it and sets the reader up for upcoming lectures concerning [http://en.wikipedia.org/wiki/Test-driven_development Test-Driven Development (TDD)]. Hence for the purpose of this article, it is assumed that the reader has already read the material and watched the video lectures before this one in the series. Hence the setup for Cucumber, Capybara and BDD basics are excluded from this article. The resources dealing with these can be found in the SaaS lectures 4.1 to 4.5 in the series. Similarly, the theory and implementation of TDD as well as further discussion on BDD is excluded from the scope of this article. More details on these can be found in lectures 4.7 to 5.11 of the SaaS lecture series .<ref>Software Engineering for SaaS, https://class.coursera.org/saas-2012-003/class/index </ref>
===What is a Closure?===
 
 
===How does it work?===


==A Brief Recap==
Behavior-Driven Development (BDD) is a specialized development process which concentrates more on the behavioral aspects of the application as against the actual implementation of the application. <ref>Software Engineering for SaaS, https://class.coursera.org/saas-2012-003/class/index </ref>
Storyboards are used to show how UI changes based on user actions. Even if it is tedious to draw sketches and storyboards, it helps build a ‘bigger picture’ understanding of the flow of the application for the non-technical stakeholders.
Cucumber is one of the shining tools of Rails that is used for testing purposes. It acts as a halfway between the customer and the developer by converting user stories of 3x5 cards into tests.
These tests act as Acceptance tests for ensuring the satisfaction of the customer. Also, the tests act as Integration tests that ensures the communication between modules is consistent and correct


===Why are closures needed?===
==The User Story==
The aim of the User story described in the video is to add a new feature to the Rotten Potatoes web page, to add a new movie to the movies list on Rotten Potatoes page. But, for adding the movie to the list, it populates the data from TMDb rather than entering the information by hand. This is aimed at reducing repetitive work since the TMDb already has a good database of movies.
To do so, the ability to search TMDb from Rotten Potatoes home page needs to be incorporated in the webpage. Users can then search in TMDb if the movie is present in it and then its details can be imported into Rotten Potatoes. For integrating this feature into the application, first, a Lo-Fi UI and Storyboard is designed, which is described next.


Although Closures are not mandatory requirements for a language, Closures provide an elegant way of binding the environment implicitly. Some of the popular languages like C, C++ and Java don't support Closures. Closures help in writing compact codes by avoiding the use of many arguments, as it closes the local variables in its surrounding.
===The Storyboard===
The [http://en.wikipedia.org/wiki/Storyboard#Software Storyboard] is to be interpreted as follows
When a person wants to add a new movie to the Movies list on Rotten Potatoes, he/she will enter the new movie name and click “Search”. The controller method should search the TMDb database for the given movie. If a match is found, i.e. the movie is present in the TMDb, then the “Match” page is displayed. If there is no match, otherwise “No Match” page is displayed. These two scenarios, are the Happy Path and Sad Path implementations respectively.
[[File:Storyboard.jpg|200 px|thumb|center|The Storyboard for the User Story. (Click Image to expand) Source : https://class.coursera.org/saas-2012-003/class/index ]]


===Other ways to use a closure===
==Happy Path, Sad Path and Bad path==
Many of us are aware of the test cases being categorized as Positive or Negative test cases. Similarly, for testing using Rails Cucumber, the tests cases are being categorized as Happy, Sad of Bad<ref>http://www.nishantverma.com/2010/03/test-case-paths-happy-sad-bad.html
</ref>. A test case that results in a positive result is called a Happy Path. E.g. On entering correct username and password on login page, the application logs the user in.
A test case that yields no result is called a Sad Path. E.g. Entering invalid username or password on login page, which normally returns an ‘Incorrect username or password’ message
A test case to handle an exceptional condition or situation, which the system should handle elegantly and show some message to the user is a Bad Path. E.g. Uploading image size exceeds a limited amount. User can then take corrective action.


Rather than calling the method <code>lambda</code>, following ways can be used to create <code>Proc</code> objects:<br><br><br>
===Elaboration of the User Story===
<b>(i)</b> Using the <code>( -> )</code> syntax <br>
Let us consider the sad path scenario for the Search TMDb User Story Feature.
<code>->params  {  . . .  } </code><br>
Example: <br>
<code>
obj = ->(v1,v2) { v1 + v2 } <br>
obj.call(2,3)  # => 5
</code><br>
Note: There cannot be any space between > and opening paranthesis. <br><br>
 
<b>(ii)</b> By passing a block to the method whose last parameter is prefixed with an ampersand (&).
That parameter will receive the block as a Proc object.
<pre>def sampmeth(v1, &block)
    puts block.inspect
end
  sampmeth(1) { "a block" }
  sampmeth(3)
</pre>
produces:
<pre>
<pre>
  #<Proc:0x0b5f5e@/tmp/prog1.rb:4>
Feature: User can add movie by searching in The Movie Database(TMDb)
  Nil
</pre><br>
User Story:
<b>(iii)</b> By calling <code>Proc.new</code> by associating it with a block<br>
As a movie fan
<pre>obj = Proc.new { "a block" }
So that I can add new movies without manual tedium
obj  #=> #<Proc:0x0a5e5a@/tmp/prog1.rb:1>
I want to add movies by looking up their details in TMDb
</pre><br>
   
 
Scenario: Try to add non-existent movie (sad path)
===Scope of a Closure===
Closure remembers the context in which it is defined, and uses that context whenever it is called. The context may include the value of self, constants, local variables, class variables and any defined block.
   
   
<pre>
Given I am on the RottenPotatoes home page
class ClosureExample
Then I should see "Search TMDb for a movie"
  CONST = 1
When I fill in "Search Terms" with "Movie That Does Not Exist"
  @@class_var = 4
And I press "Search TMDb"
Then I should be on the RottenPotatoes home page
And I should see "'Movie That Does Not Exist' was not found in TMDb."
</pre>


  def return_closure
The Test shown above has 6 steps (The logic and code for implementing this is provided in the TDD lectures in the Coursera series).
  local_var = 2
[[File:RottenPotatoes1.jpg|200 px|thumb|center|The Cucumber Test with Features and Scenarios. (Click Image to expand) https://class.coursera.org/saas-2012-003/class/index ]]
  @instance_var = 3
  lambda { [ CONST, local_var, @instance_var,  @@class_var, yield ] }
  end


  def update_values
*First the test checks if the user is on the Home Page, viz. the Home page for the application exists.
    @instance_var += 1
*Next, it checks for presence of the link to “Search TMDb for movie”. The test essentially simulates what a normal user would do when testing the application manually.
    @@class_var += 1
*In the third step, the test populates the form for the Search, with a movie name that is not present in the TMDb (since this is a sad path implementation, the search should essentially fail.)
  end
*Next, the test checks (simulates) if the link can be clicked.
end
*As a consequence of this, the user should navigate to the Rotten Potatoes home page.
*For a Sad path, the user is shown a message mentioning that there was no match, on the home page itself.


ex = ClosureExample.new
When we run the test, since there is already a home page, the first step of the scenario passes.  
block = ex.return_closure { "dummy" }
[[File:RottenPotatoes2.jpg|200 px|thumb|center|Step for Home Page Passes. (Click Image to expand) https://class.coursera.org/saas-2012-003/class/index]]
block.call    # => [ 1, 2, 3, 4, "dummy" ]
In the second step of the scenario, there is no link on the view for adding a new movie. Hence the second step fails and the following steps are skipped.  
ex.update_values
[[File:RottenPotatoes3.jpg|200 px|thumb|center|However Step for the "search_tmdb" link fails and others after that are skipped. (Click Image to expand) https://class.coursera.org/saas-2012-003/class/index]]
block.call    # => [ 1, 2, 4, 5, "dummy" ]


</pre>


In the above example, the return_closure method returns a lambda that accesses local variable <code>local_var</code>, instance variable <code>instance_var</code>, class variable <code>class_var</code> and constant <code>CONST</code>. Even if the block is not in the scope of the object that contains these values, it is still accessible via the closure. So, when we update these values calling the update_values method, the values accessed via the closure also get updated.
Now, we add the link for the “search_tmdb” action. For this, the [http://en.wikipedia.org/wiki/HAML HAML] for Search TMDb page should contain the following:
Closure is a very handy feature for Ruby developers and is used extensively. The rest of the chapter describes the implementation of Closures in different popular programming languages.
<br>
<br>
==Closures in other languages==
A closure or a closure-like implementation is available in most programming languages. Now that we have seen the implementation of closures in Ruby, let us explore the use of closures in other programming languages.<br>
===Closures in C#===
A closure in C# can be done using Lambda expressions and delegates, where a first-class function[http://en.wikipedia.org/wiki/First-class_function] references the variables in the surrounding scope. Such a referenced variable is neither a parameter of the function nor a local variable<ref>Hilyard, J. and Teilhet,S., "C# 3.0 Cookbook 3rd edition", O'reilly Pub. (2007)</ref>.
For example,
<pre>
<pre>
  static void Main(string[] args)
%h1 Search TMDb for a movie
  {
   
var obj = ClosureFunc(1);
= form_tag :action => 'search_tmdb' do
Console.Writeline("Output 1= " + obj(2));
   
Console.Writeline("Output 2= " + obj(3));
  %label{:for => 'search_terms'} Search Terms
}
  = text_field_tag 'search_terms'
public static Func<int, int> ClosureFunc(inc)
  = submit_tag 'Search TMDb'
  {
Func<int,int> SampleFunc = delegate(int val)
{
inc = inc + 1;
return val + inc;
};
return SampleFunc;
}   
</pre>
The result obtained is:
<pre>
Output 1= 4
Output 2= 6
</pre>
</pre>
Now, when we call <code>ClosureFunc</code> from <code>Main()</code>,we get a method back that increments a local variable inside the method. <code>inc</code> is a local variable of <code>ClosureFunc()</code> which is accessed inside of the delegate. So, when we call the method twice, the local variable <code>inc</code> gets incremented twice outside of its original scope.
What actually happens here is the C# compiler encapsulates the delegate and the associated local variables into a compiler generated class. So, on invoking the delegate, it creates a lambda expression to increment the value of <code>inc</code> and results in calling a method on this class.
Another way to write the ClosureFunc is using <code><b>lambda</b> ( => ) </code>
  <pre>Func<string,string> ClosureFunc = val => val + inc;</pre>


===Closures in JavaScript===
These are added into the end of <code>app/views/movies/index.html.haml</code>
JavaScript is a dynamic and powerful scripting language which allows the use of Closures. An example of a closure in JavaScript is as follows<ref>Bhattacharya, A. , Sunder, K. , "Memory leak patterns in JavaScript", http://www.ibm.com/developerworks/web/library/wa-memleak/index.html</ref>:<pre>
  function ClosureFunc(inc)
  {
        return  SampleFunc (val) {
              return val + inc;
  }
  var obj = ClosureFunc(10);
  obj(5);  // returns 15
</pre>


Here, when a call is made to the outer function <code>ClosureFunc</code> with a parameter <code>inc</code>, the function returns with a pointer to the inner function <code>SampleFunc</code>, which is assigned to the variable <code>obj</code>.
The last two lines in the above code will be expanded into the HTML code:


===Closures in Perl===
Perl also supports closures extensively. Syntax for implementing closure in Perl<ref> Cozens, S., "Achieving Closure", http://www.perl.com/pub/2002/05/29/closure.html</ref> is similar to that of Ruby, which is as follows:
<pre>
<pre>
    sub closurefunc {
<label for='search_terms'>Search Terms</label>
my $inc = shift;
   
my $obj = sub {
<input id="search_terms" name="search_terms"type="text" />
my $val = shift;
return $val + $inc;    # uses local variable inc
};
return $obj;
  }
 
  my $add3 = closurefunc(3);
  print "5 plus 3 is ":
  print $add3->(5);          #  => 8
</pre>
</pre>
Here, the <code>closurefunc</code> subroutine creates an anonymous function, which uses the local variable <code>inc</code> and returns the object <code>$obj</code>.


===Closures in Python===
Now there is a page and a link in place for the test. On running the test using Cucumber again, the test fails again. This is because there is still no method defined in the controller (movies_controller.rb) for actually searching in TMDb.


Closures in python<ref>"Python Closures explained", http://www.shutupandship.com/2012/01/python-closures-explained.html</ref> are implemented as function calls. Support for closures has existed in Python since version 2.2, although the syntax of Python closures can actually confuse the user in thinking that the closures are not supported.
[[File:RottenPotatoes4.jpg|200 px|thumb|center|The Link for search_tmdb is added to the view, but there is no method for search_tmdb in controller. Also there is no route. Hence these steps fail or are skipped. (Click Image to expand) https://class.coursera.org/saas-2012-003/class/index]]


Consider following example code for creating an 'closurefunc' function using closures<ref>"Closures in Python : examples implementing Closures in Python", http://ynniv.com/blog/2007/08/closures-in-python.html</ref>:
However the video tutorial in this discussion considers only the sad path scenario. To test this Sad path implementation, a dummy/fake method is defined in the movies_controller.rb which fails everytime, i.e. the search for the movie in TMDb never returns a match.  
   
   
So, the following code for the fake controller method is added to the movies_controller.rb:
<pre>
<pre>
def closurefunc(x):
def search_tmdb
def inc(y):
  # hardwire to simulate failure
    # x is "closed" in the definition of inc
  flash[:warning] = "'#{params[:search_terms]}' was not found in TMDb."
    return y + x
  redirect_to movies_path
 
end
return inc
 
inc5 = closurefunc(5)
inc10 = closurefunc(10)
 
inc5 (5) # returns 10
</pre>
</pre>


As mentioned above, closures in Python are executed as function calls.  
and since a new method has been added, we need to add a route to this controller method too. The following route is added to routes.db, just before or just after <code>‘resources :movies’</code>:
The call to <code><b>closurefunc</b></code> creates a binding for <code>x</code> which is referenced inside the function <code>inc</code>. In technical terms, the function <code>inc</code> is closed over the variable <code><b>x</b></code>.
 
If we go ahead and delete the <code><b>closurefunc</b></code> function from the global namespace,
<pre>
<pre>
del closurefunc
# Route that posts 'Search TMDb' form
post '/movies/search_tmdb'
</pre>
</pre>


even then,  
Now, on running the test again using Cucumber, every time the method is called, it returns a failure message (since it is hard-coded). This implies that the sad path implementation passed.
<pre>
[[File:RottenPotatoes5.jpg|200 px|thumb|center|Dummy method added to controller for 'search_tmdb' and then a route is also added. The dummy method fails everytime thereby satisfying the sad path scenario. (Click Image to expand) https://class.coursera.org/saas-2012-003/class/index]]
>>inc10(5) # returns 15
</pre>


Why does this happen? Each call to <code><b>closurefunc</b></code> creates a new instance, returning an <code>inc</code> function object and assigning it to the <code>inc5</code> and <code>inc10</code>. Each instance has a link to a different binding of <code>x</code>. Hence, the function <code>inc</code> will have access to <code>x</code> even if the scope where <code>x</code> was defined doesn’t exist anymore.
The actual code and logic for implementation of this action (search_tmdb) is discussed in later lectures which discuss the TDD (Test Driven Development) process


The above example shows the closure of <code>x</code> being used to eliminate either a global or a constant, depending on the nature of <code>x</code>.
So far we have considered the Sad path scenario.
Let us consider the happy path scenario for the Search TMDb User Story Feature.
<pre>  
Feature: User can add movie by searching in The Movie Database(TMDb)
User Story:
As a movie fan
So that I can add new movies without manual tedium
I want to add movies by looking up their details in TMDb
Scenario: Try to add existing movie (happy path)
  When I fill in "Search Terms" with "Inception"
  And I press "Search TMDb"
  Then I should be on the RottenPotatoes home page
  And I should see "Inception"
  And I should see "PG-13"
</pre>


Python also supports the use of closures through the <code><b>lambda</b></code> constructs.
For implementation of this, instead of the hard coded Controller method, an actual controller method will have to written which searches TMDb and returns the details of the movie which will then be added to the Rotten Potatoes database. (However, this implementation is out of scope for our article. Further details of this can be found in the TDD lectures of this series.)


The difference between a normal function and a <code><b>lambda</b></code> function is shown below
Now, the Happy and the Sad paths have some code in common which violates the [http://en.wikipedia.org/wiki/Don't_repeat_yourself DRY principle]. So, to make it DRY, Cucumber offers a solution called Background <ref>Background in Cucumber, https://github.com/cucumber/cucumber/wiki/Background</ref>. The steps which are common to both the paths are written in Background and those steps will run in the background before all the other scenarios. No matter what scenario one runs, the steps in the Background will run first, which will DRY out the code. This also helps in reducing the effort in case any steps leading up to the scenarios (setup) are to be modified. The tester will only have to change the code in one location. In this example there is only one happy path and sad path scenario  i.e. only two scenarios. However, in real world examples, there would be a larger list of scenarios which could have the same ‘setup’. The advantages of the Background facility are more pronounced in this case.
<pre>
[[File:BackgroundCucumber.jpg|200 px|thumb|center|The Cucumber Background. (Click Image to expand) https://class.coursera.org/saas-2012-003/class/index]]


>>> def f (x): return x**2  #normal function definition
==Summary==
...
>>> print f(8)
64


>>> g = lambda x: x**2      #demo of lambda construct
In summary, this article demonstrates the BDD/Cucumber test behaviour with the help of examples. It discusses the enhancement of Rotten Potatoes webpage for implementing a new feature, which is addition of a new movie by looking up the TMDb database rather than manually entering the movie name. This includes designing a Lo-Fi UI, writing scenarios and step definitions, even writing new methods for successfully executing the test cases using Cucumber. The article also introduces and explains the concept of Background in Cucumber, which helps in DRYing out the repeated code in scenarios of the same feature. The next chapter will discuss the TDD/RSpec behaviour and the approach to make all the scenarios pass.
>>>
>>> print g(8)
64
</pre>


As you can see, the <code><b>lambda</b></code> function is defined on the fly and always returns an expression. If we write a new <code><b>closurefunc</b></code> function using the <code><b>lambda</b></code> construct, we will see the following
== Topical References ==
<pre>
 
>>def closurefunc(n): return lambda x: x + n #creation of lambda function


>>f = closurefunc(2)
>>g = closurefunc(6)
>>print f(42), g(42)
>>44, 48
</pre>
The lambda function is created on the fly and returns an expression which is assigned to <code>f</code> and <code>g</code>. The <code><b>lambda</b></code> construct works even without assigning the returned expression, as shown next
<pre>
>>print closurefunc(22)(33)
>>55
</pre>
<br>
==Conclusion==
In conclusion, a brief introduction to closures and its usage in dynamic scripting languages has been provided above. The code examples illustrate the syntax and semantics supported by these languages. A concise comparison between the implementation in these languages is summarized below. References are provided for the users to enhance their understanding of the subject. <br>
{| class="wikitable"
|-
!  !! Ruby !! C# !! JavaScript !! Perl !! Python !! C / C++ / Java
|-
| <b>Support for Closures</b>|| Yes|| Yes|| Yes|| Yes|| Yes|| No
|-
| <b>Closure implementation using</b>|| Proc, block and Lambdas || Delegates and First Class functions || Unnamed functions || Anonymous functions || Named function calls, Lambda|| No
|-
| <b>Support for Lambda implementation</b>|| Yes|| Yes|| No|| No direct support, can be simulated using anonymous functions|| Yes|| No
|}
<br>
== Topical References ==


<references/>
<references/>
<br>
== Further Reading ==
== Further Reading ==
[https://github.com/jnicklas/capybara Capybara] : Capybara introduction .


[http://railscasts.com/episodes/275-how-i-test Railscast on testing in Capybara] : Railscast on testing in Capybara.


[http://en.wikipedia.org/wiki/Closure_(computer_science) Wikipedia - Closures] : everything there is to know about Closures on one page.
[http://expertiza.csc.ncsu.edu/wiki/index.php/CSC/ECE_517_Fall_2012/ch1b_1w71_gs Capybara and Cucumber] : Capybara and Cucumber.
 
[http://weblog.raganwald.com/2007/01/closures-and-higher-order-functions.html Reg Braithwaite - Closures and Higher-Order Functions] : gives Ruby examples of specialized Closures.
 
[http://ruby.runpaint.org/closures Closures in Ruby] : gives a detailed explanation of Closures in Ruby.
 
[http://msdn.microsoft.com/en-us/magazine/ee309512.aspx MSDN Magazine - Functional Programming .NET Development] : examples of functional style with Closures in C#.
 
[http://www.randomhacks.net/articles/2007/02/01/some-useful-closures-in-ruby Eric Kidd - Some useful closures, in Ruby] : demonstrates more examples of curried and specialized Closures in Ruby.
 
[http://onestepback.org/articles/invitationtoruby/reason4.html Jim Weirich - Top Ten Reasons I Like Ruby - Blocks and Closures] : provides several examples of how Closures can be useful in Ruby.


[http://csharpindepth.com/Articles/Chapter5/Closures.aspx C# in Depth - The Beauty of Closures] : demonstrates Closures in C#.
[http://blog.objectmentor.com/articles/2008/11/27/the-truth-about-bdd The Truth about BDD] : The Truth about BDD.


[http://martinfowler.com/bliki/Closure.html Martin Fowler - Closure] : provides a basic definition of Closures and gives a few very basic
[http://benmabey.com/2008/05/10/slides-and-code-from-my-bddrspec-presentation.html BDD and RSpec] : BDD and RSpec.
examples.  


[http://jibbering.com/faq/notes/closures Javascript Closures] : demonstrates Javascript Closures in callbacks.
[http://www.allenwei.cn/cucumber-capybara-what-we-need-for-rails-integration-test/ Cucumber and Capybara for Rails] : Cucumber and Capybara for Rails.


[http://www.skorks.com/2010/05/closures-a-simple-explanation-using-ruby/ Alan Skorkin - Closures - A Simple Explanation] : gives a high-level overview of Closures using Ruby examples.
[http://rubysource.com/smelly-cucumbers/ Smelly Cucumbers] : Smelly Cucumbers.


[http://www.ibm.com/developerworks/java/library/j-cb01097/index.html IBM Developerworks - Crossing borders : Closures] : gives Ruby examples where Closures are useful.
[http://cukes.info/ Cucumber] : Cucumber.


[http://samdanielson.com/2007/9/6/an-introduction-to-closures-in-ruby Sam Danielson - An Introduction to Closures in Ruby] : high-level overview of using Closures in Ruby.
[http://railscasts.com/episodes/155-beginning-with-cucumber Beginning with Cucumber] : Testing with Cucumber.

Latest revision as of 17:51, 27 October 2012

SaaS 4.6 Enhancing Rotten Potatoes again

Introduction

The article is intended to be an accompaniment to the SaaS video lecture 4.6  titled “Enhancing Rotten Potatoes again” <ref name="video">Enhancing Rotten Potatoes again, http://www.youtube.com/watch?v=2l9uLw3y6J8
</ref>, part of the SaaS video lecture series for the Software Engineering for SaaS hosted on the Coursera learning network. <ref>Coursera, https://www.coursera.org/</ref>
The Rotten Potatoes webpage, designed in the previous lectures is enhanced for implementing a new feature to add a new movie by looking up the TMDb database rather than manually entering the movie name. The flow of this article is as follows :

  • The scope of discussion
  • Brief description of the storyboard used for the Lo-Fi UI design discussed in previous lectures in the video lecture series,
  • Explanation of Sad path, Happy Path and Bad Path
  • Description of the User Story developed in the video lecture
  • Elaboration of the test scenarios written for the particular User story (including scenarios for happy and sad paths) in Cucumber
  • Explanation of the Background facility in Cucumber

Note : All images used in this article have been taken from screen captures of Coursera video lectures available here. All content is owned by original authors and there is no copyright infringement intended. References have been provided in the image descriptions wherever necessary

Scope

The scope of this article is limited only to the scope covered in the video lecture. The lecture builds upon Behavior-Driven Development (BDD) basics discussed in the video lectures before it and sets the reader up for upcoming lectures concerning Test-Driven Development (TDD). Hence for the purpose of this article, it is assumed that the reader has already read the material and watched the video lectures before this one in the series. Hence the setup for Cucumber, Capybara and BDD basics are excluded from this article. The resources dealing with these can be found in the SaaS lectures 4.1 to 4.5 in the series. Similarly, the theory and implementation of TDD as well as further discussion on BDD is excluded from the scope of this article. More details on these can be found in lectures 4.7 to 5.11 of the SaaS lecture series .<ref>Software Engineering for SaaS, https://class.coursera.org/saas-2012-003/class/index </ref>

A Brief Recap

Behavior-Driven Development (BDD) is a specialized development process which concentrates more on the behavioral aspects of the application as against the actual implementation of the application. <ref>Software Engineering for SaaS, https://class.coursera.org/saas-2012-003/class/index </ref> Storyboards are used to show how UI changes based on user actions. Even if it is tedious to draw sketches and storyboards, it helps build a ‘bigger picture’ understanding of the flow of the application for the non-technical stakeholders.

Cucumber is one of the shining tools of Rails that is used for testing purposes. It acts as a halfway between the customer and the developer by converting user stories of 3x5 cards into tests. These tests act as Acceptance tests for ensuring the satisfaction of the customer. Also, the tests act as Integration tests that ensures the communication between modules is consistent and correct

The User Story

The aim of the User story described in the video is to add a new feature to the Rotten Potatoes web page, to add a new movie to the movies list on Rotten Potatoes page. But, for adding the movie to the list, it populates the data from TMDb rather than entering the information by hand. This is aimed at reducing repetitive work since the TMDb already has a good database of movies. To do so, the ability to search TMDb from Rotten Potatoes home page needs to be incorporated in the webpage. Users can then search in TMDb if the movie is present in it and then its details can be imported into Rotten Potatoes. For integrating this feature into the application, first, a Lo-Fi UI and Storyboard is designed, which is described next.

The Storyboard

The Storyboard is to be interpreted as follows When a person wants to add a new movie to the Movies list on Rotten Potatoes, he/she will enter the new movie name and click “Search”. The controller method should search the TMDb database for the given movie. If a match is found, i.e. the movie is present in the TMDb, then the “Match” page is displayed. If there is no match, otherwise “No Match” page is displayed. These two scenarios, are the Happy Path and Sad Path implementations respectively.

The Storyboard for the User Story. (Click Image to expand) Source : https://class.coursera.org/saas-2012-003/class/index

Happy Path, Sad Path and Bad path

Many of us are aware of the test cases being categorized as Positive or Negative test cases. Similarly, for testing using Rails Cucumber, the tests cases are being categorized as Happy, Sad of Bad<ref>http://www.nishantverma.com/2010/03/test-case-paths-happy-sad-bad.html </ref>. A test case that results in a positive result is called a Happy Path. E.g. On entering correct username and password on login page, the application logs the user in. A test case that yields no result is called a Sad Path. E.g. Entering invalid username or password on login page, which normally returns an ‘Incorrect username or password’ message A test case to handle an exceptional condition or situation, which the system should handle elegantly and show some message to the user is a Bad Path. E.g. Uploading image size exceeds a limited amount. User can then take corrective action.

Elaboration of the User Story

Let us consider the sad path scenario for the Search TMDb User Story Feature.

Feature: User can add movie by searching in The Movie Database(TMDb)
 
User Story:
As a movie fan
So that I can add new movies without manual tedium
I want to add movies by looking up their details in TMDb
 
Scenario: Try to add non-existent movie (sad path)
 
Given I am on the RottenPotatoes home page
Then I should see "Search TMDb for a movie"
When I fill in "Search Terms" with "Movie That Does Not Exist"
And I press "Search TMDb"
Then I should be on the RottenPotatoes home page
And I should see "'Movie That Does Not Exist' was not found in TMDb."

The Test shown above has 6 steps (The logic and code for implementing this is provided in the TDD lectures in the Coursera series).

The Cucumber Test with Features and Scenarios. (Click Image to expand) https://class.coursera.org/saas-2012-003/class/index
  • First the test checks if the user is on the Home Page, viz. the Home page for the application exists.
  • Next, it checks for presence of the link to “Search TMDb for movie”. The test essentially simulates what a normal user would do when testing the application manually.
  • In the third step, the test populates the form for the Search, with a movie name that is not present in the TMDb (since this is a sad path implementation, the search should essentially fail.)
  • Next, the test checks (simulates) if the link can be clicked.
  • As a consequence of this, the user should navigate to the Rotten Potatoes home page.
  • For a Sad path, the user is shown a message mentioning that there was no match, on the home page itself.

When we run the test, since there is already a home page, the first step of the scenario passes.

Step for Home Page Passes. (Click Image to expand) https://class.coursera.org/saas-2012-003/class/index

In the second step of the scenario, there is no link on the view for adding a new movie. Hence the second step fails and the following steps are skipped.

However Step for the "search_tmdb" link fails and others after that are skipped. (Click Image to expand) https://class.coursera.org/saas-2012-003/class/index


Now, we add the link for the “search_tmdb” action. For this, the HAML for Search TMDb page should contain the following:

%h1 Search TMDb for a movie
 
= form_tag :action => 'search_tmdb' do
 
  %label{:for => 'search_terms'} Search Terms
  = text_field_tag 'search_terms'
  = submit_tag 'Search TMDb'

These are added into the end of app/views/movies/index.html.haml

The last two lines in the above code will be expanded into the HTML code:

<label for='search_terms'>Search Terms</label>
 
<input id="search_terms" name="search_terms"type="text" />

Now there is a page and a link in place for the test. On running the test using Cucumber again, the test fails again. This is because there is still no method defined in the controller (movies_controller.rb) for actually searching in TMDb.

The Link for search_tmdb is added to the view, but there is no method for search_tmdb in controller. Also there is no route. Hence these steps fail or are skipped. (Click Image to expand) https://class.coursera.org/saas-2012-003/class/index

However the video tutorial in this discussion considers only the sad path scenario. To test this Sad path implementation, a dummy/fake method is defined in the movies_controller.rb which fails everytime, i.e. the search for the movie in TMDb never returns a match.

So, the following code for the fake controller method is added to the movies_controller.rb:

def search_tmdb
  # hardwire to simulate failure
  flash[:warning] = "'#{params[:search_terms]}' was not found in TMDb."
  redirect_to movies_path
end

and since a new method has been added, we need to add a route to this controller method too. The following route is added to routes.db, just before or just after ‘resources :movies’:

# Route that posts 'Search TMDb' form
post '/movies/search_tmdb'

Now, on running the test again using Cucumber, every time the method is called, it returns a failure message (since it is hard-coded). This implies that the sad path implementation passed.

Dummy method added to controller for 'search_tmdb' and then a route is also added. The dummy method fails everytime thereby satisfying the sad path scenario. (Click Image to expand) https://class.coursera.org/saas-2012-003/class/index

The actual code and logic for implementation of this action (search_tmdb) is discussed in later lectures which discuss the TDD (Test Driven Development) process

So far we have considered the Sad path scenario.

Let us consider the happy path scenario for the Search TMDb User Story Feature.

 
Feature: User can add movie by searching in The Movie Database(TMDb)
 
User Story:
As a movie fan
So that I can add new movies without manual tedium
I want to add movies by looking up their details in TMDb
 
Scenario: Try to add existing movie (happy path)
 
  When I fill in "Search Terms" with "Inception"
  And I press "Search TMDb"
  Then I should be on the RottenPotatoes home page
  And I should see "Inception"
  And I should see "PG-13"
 

For implementation of this, instead of the hard coded Controller method, an actual controller method will have to written which searches TMDb and returns the details of the movie which will then be added to the Rotten Potatoes database. (However, this implementation is out of scope for our article. Further details of this can be found in the TDD lectures of this series.)

Now, the Happy and the Sad paths have some code in common which violates the DRY principle. So, to make it DRY, Cucumber offers a solution called Background <ref>Background in Cucumber, https://github.com/cucumber/cucumber/wiki/Background</ref>. The steps which are common to both the paths are written in Background and those steps will run in the background before all the other scenarios. No matter what scenario one runs, the steps in the Background will run first, which will DRY out the code. This also helps in reducing the effort in case any steps leading up to the scenarios (setup) are to be modified. The tester will only have to change the code in one location. In this example there is only one happy path and sad path scenario i.e. only two scenarios. However, in real world examples, there would be a larger list of scenarios which could have the same ‘setup’. The advantages of the Background facility are more pronounced in this case.

The Cucumber Background. (Click Image to expand) https://class.coursera.org/saas-2012-003/class/index

Summary

In summary, this article demonstrates the BDD/Cucumber test behaviour with the help of examples. It discusses the enhancement of Rotten Potatoes webpage for implementing a new feature, which is addition of a new movie by looking up the TMDb database rather than manually entering the movie name. This includes designing a Lo-Fi UI, writing scenarios and step definitions, even writing new methods for successfully executing the test cases using Cucumber. The article also introduces and explains the concept of Background in Cucumber, which helps in DRYing out the repeated code in scenarios of the same feature. The next chapter will discuss the TDD/RSpec behaviour and the approach to make all the scenarios pass.

Topical References

<references/>

Further Reading

Capybara : Capybara introduction .

Railscast on testing in Capybara : Railscast on testing in Capybara.

Capybara and Cucumber : Capybara and Cucumber.

The Truth about BDD : The Truth about BDD.

BDD and RSpec : BDD and RSpec.

Cucumber and Capybara for Rails : Cucumber and Capybara for Rails.

Smelly Cucumbers : Smelly Cucumbers.

Cucumber : Cucumber.

Beginning with Cucumber : Testing with Cucumber.