CSC/ECE 517 Fall 2012/ch2a 2w31 up: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
Line 35: Line 35:


===== Controller Action : Setup =====
===== Controller Action : Setup =====
#Adding the route to <span style="color:blue"> config/routes.rb </span>:<br/>To add a new feature to this Rails application, we first add a route, which maps a URL to the controller method <ref>[http://guides.rubyonrails.org/routing.html Routing in Rails]</ref> <br/><br/><code><span style="color:grey"># Route that posts 'Search TMDb' form </span><br/><span style="color:blue"> post </span> '/<span style="color:red">movies</span>/<span style="color:green">search_tmdb</span>'</code><br/>This route would map to <code> <span style="color:red">Movies</span><span style="color:blue">Controller#</span><span style="color:green">search_tmdb</span></code> owing to [http://en.wikipedia.org/wiki/Convention_over_configuration Convention over Configuration], that is, it would post to the search_tmdb "action" in the Movies "controller".<br/><br/>
#Add the route to <span style="color:blue"> config/routes.rb </span>:<br/>To add a new feature to this Rails application, we first add a route, which maps a URL to the controller method <ref>[http://guides.rubyonrails.org/routing.html Routing in Rails]</ref> <br/><br/><code><span style="color:grey"># Route that posts 'Search TMDb' form </span><br/><span style="color:blue"> post </span> '/<span style="color:red">movies</span>/<span style="color:green">search_tmdb</span>'</code><br/>This route would map to <code> <span style="color:red">Movies</span><span style="color:blue">Controller#</span><span style="color:green">search_tmdb</span></code> owing to [http://en.wikipedia.org/wiki/Convention_over_configuration Convention over Configuration], that is, it would post to the search_tmdb "action" in the Movies "controller".<br/><br/>
#Create an empty view: <br/> When a controller method is triggered, it gets some user input, does some computation and renders a view. So, the invocation of a controller needs a view to render, which we need to create even though it is not required to be tested. We start with an empty view. "[http://en.wikipedia.org/wiki/Touch_(Unix) touch]" unix command is used to create a file of size 0 bytes. <br/> <br/><code><span style="color:blue">touch app/views/<span style="color:red">movies</span>/<span style="color:green">search_tmdb</span>.html.haml </span></code><br/><br/>The above creates a view in the right directory with the file name, same as Movie controller's method name. (Convention over Configuration) <br/>This view can be refined in later iterations and user stories are used to verify if the view has everything that is needed.<br/><br/>
#Create an empty view: <br/> When a controller method is triggered, it gets some user input, does some computation and renders a view. So, the invocation of a controller needs a view to render, which we need to create even though it is not required to be tested. We start with an empty view. "[http://en.wikipedia.org/wiki/Touch_(Unix) touch]" unix command is used to create a file of size 0 bytes. <br/> <br/><code><span style="color:blue">touch app/views/<span style="color:red">movies</span>/<span style="color:green">search_tmdb</span>.html.haml </span></code><br/><br/>The above creates a view in the right directory with the file name, same as Movie controller's method name. (Convention over Configuration) <br/>This view can be refined in later iterations and user stories are used to verify if the view has everything that is needed.<br/><br/>
#Replace fake “hardwired” method in <span style="color:blue">movies_controller.rb</span> with empty method: <br/>If the method has a default functionality to return an empty list, then replace the method to one that does nothing.<br/><br/><code>def search_tmdb<br/>end</code><br/>
#Replace fake “hardwired” method in <span style="color:blue">movies_controller.rb</span> with empty method: <br/>If the method has a default functionality to return an empty list, then replace the method to one that does nothing.<br/><br/><code>def search_tmdb<br/>end</code><br/>
Line 42: Line 42:
It is the responsibility of the model to call TMDb and search for movies. But, no model method exists as yet to do this.<br/>
It is the responsibility of the model to call TMDb and search for movies. But, no model method exists as yet to do this.<br/>
One may wonder that to test the controller's functionality, one has to get the model method working. Nevertheless, that is not required.<br/>
One may wonder that to test the controller's functionality, one has to get the model method working. Nevertheless, that is not required.<br/>
Seam is used in this case, to test the code we wish we had(“<span style="color:red">CWWWH</span>”). Let us call the method name as <span style="color:blue">
Seam is used in this case, to test the code we wish we had(“<span style="color:red">CWWWH</span>”). Let us call the "non-existent" model method as <span style="color:blue">Movie.find_in_tmdb</span>


•  No problem...weʼll use a seam to test the code we
=====Testing plan=====
wish we had (“CWWWH”), Movie.find_in_tmdb
#Simulate POSTing search form to controller action.
•  Game plan: !
#Check that controller action tries to call <span style="color:blue">Movie.find_in_tmdb</span> with the function argument as data from the submitted form. Here, the functionality of the model is not tested, instead the test ensures the controller invokes the right method with the right arguments.
#The test will fail (<span style="color:red">red</span>), because the (empty) controller method doesnʼt call find_in_tmdb.
#Fix controller action to make the test pass (<span style="color:green">green</span>).


–  Simulate POSTing search form to controller action.!
=====Test MoviesController : Code<ref>[http://pastebin.com/zKnwphQZ TMDb : MoviesController Test Code]</ref>=====
–  Check that controller action tries to call
Movie.find_in_tmdb with data from submitted form.!
–  The test will fail (red), because the (empty) controller
method doesnʼt call find_in_tmdb.!
–  Fix controller action to make green.!


http://pastebin.com/zKnwphQZ
<pre>
It is actually the model method that
require 'spec_helper'
describe MoviesController do
  describe 'searching TMDb' do
    it 'should call the model method that performs TMDb search' do
      Movie.should_receive(:find_in_tmdb).with('hardware')
      post :search_tmdb, {:search_terms => 'hardware'}
    end
  end
end
</pre>
 
The above test case for the MoviesController has an 'it' block, which has a string defining what the test is supposed to check. A do-end block to that 'it' has the actual test code. <br/>
 
The line  <code><b>Movie</b>.<span style="color:violet">should_receive</span>(<span style="color:red">:find_in_tmdb</span>).<span style="color:violet">with</span>('<span style="color:brown">hardware</span>')</code>  creates an expectation that the <b>Movie</b> class should receive the <span style="color:red">find_in_tmdb</span> method call with a particular argument. An assumption is made here, that the user has actually filled in <span style="color:brown">hardware</span> in the search_terms box on the page that says Search for TMDb. <br/>
 
Once the expectation is setup, we simulate the post. 'post' is an [http://rspec.info/ RSpec] extension (that is a part of [http://rubydoc.info/gems/rspec-rails/frames RSpec Rails)


== References ==
== References ==

Revision as of 01:31, 22 October 2012

SaaS - 5.3 - The TDD cycle: red-green-refactor

Introduction

Test Driven Development is an evolutionary approach to software development that requires a developer to write test code before the actual code and then write the minimum code to pass that test. This process is done iteratively to ensure that all units in the application are tested for optimum functionality, both individually and in synergy with others. This produces applications of high quality in less time.

Advantages and Disadvantages

Advantages

ensures the code is tested and enables you to retest your code quickly and easily, since it’s automated.

SEAMS

The concept of CWWWH(code we wish we had) is about a missing piece of code in TDD. In test driven development, we generally write a test and then the implement the functionality. But it may happen that the program which implements a certain feature, is dependent on some other feature which is not yet implemented. That piece of code named as "CWWWH". The test is supposed to fail in such situation. But it does'nt because we have a concept called SEAMS which is a technical name assigned by Michael Feather's in his book WorkingEffectivelyWithLegacyCode <ref> www.objectmentor.com/.../WorkingEffectivelyWithLegacyCode...</ref> A seam is a place where one can alter behavior in your program without editing in that place. A virtual method can be especially useful in legacy code to create a point of extensibility to test code, where we cannot reasonably extract or introduce an interface to allow us to do so.

RedGreen – Refactor

Add a Test

  • Think about one thing the code should do : The developer identifies a new functionality from the use cases and user stories, which contain detailed requirements and constraints.
  • Capture that thought in a test, which fails  : An automated test case (new or a variant of an existing test) is then written, corresponding to the new feature, taking into consideration all possible inputs, error conditions and outputs. Run all the automated tests. The new test inevitably fails because it is written prior to the implementation of the feature. This validates that the feature is rightly tested and would pass if the feature is implemented correctly, which drives a developer to the next step.

Implement the feature

  • Write the simplest possible code that lets the test pass  : Minimal code is written to make the test pass. The entire functionality need not be implemented in this step. It is not uncommon to see empty methods or methods that simply return a constant value. The code can be improved in the next iterations. Future tests will be written to further define what these methods should do. The only intention of the developer is to write "just enough" code to ensure it meets all the tested requirements and doesn't cause any other tests to fail. <ref>What is Test Driven Development?</ref> Run the tests again. Ideally, all the tests should pass, making the developer confident about the features implemented so far.

Refactor

  • DRY out commonality with other tests : Remove duplication of code wherever possible. Organizational changes can be made as well to make the code appear cleaner so it’s easier to maintain. TDD encourages frequent refactoring. Automated tests can be run to ensure the code refactoring did not break any existing functionality

Iterate

  • Continue with the next thing (new or improvement of a feature), the code should do.
  • Aim to have working code always.

Examples

TMDb : The Movie Database rails application

New Feature : Search TMDb for movies

Controller Action : Setup
  1. Add the route to config/routes.rb :
    To add a new feature to this Rails application, we first add a route, which maps a URL to the controller method <ref>Routing in Rails</ref>

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

    This route would map to MoviesController#search_tmdb owing to Convention over Configuration, that is, it would post to the search_tmdb "action" in the Movies "controller".

  2. Create an empty view:
    When a controller method is triggered, it gets some user input, does some computation and renders a view. So, the invocation of a controller needs a view to render, which we need to create even though it is not required to be tested. We start with an empty view. "touch" unix command is used to create a file of size 0 bytes.

    touch app/views/movies/search_tmdb.html.haml

    The above creates a view in the right directory with the file name, same as Movie controller's method name. (Convention over Configuration)
    This view can be refined in later iterations and user stories are used to verify if the view has everything that is needed.

  3. Replace fake “hardwired” method in movies_controller.rb with empty method:
    If the method has a default functionality to return an empty list, then replace the method to one that does nothing.

    def search_tmdb
    end

What model method?

It is the responsibility of the model to call TMDb and search for movies. But, no model method exists as yet to do this.
One may wonder that to test the controller's functionality, one has to get the model method working. Nevertheless, that is not required.
Seam is used in this case, to test the code we wish we had(“CWWWH”). Let us call the "non-existent" model method as Movie.find_in_tmdb

Testing plan
  1. Simulate POSTing search form to controller action.
  2. Check that controller action tries to call Movie.find_in_tmdb with the function argument as data from the submitted form. Here, the functionality of the model is not tested, instead the test ensures the controller invokes the right method with the right arguments.
  3. The test will fail (red), because the (empty) controller method doesnʼt call find_in_tmdb.
  4. Fix controller action to make the test pass (green).
Test MoviesController : Code<ref>TMDb : MoviesController Test Code</ref>
require 'spec_helper'
describe MoviesController do
  describe 'searching TMDb' do
    it 'should call the model method that performs TMDb search' do
      Movie.should_receive(:find_in_tmdb).with('hardware')
      post :search_tmdb, {:search_terms => 'hardware'}
    end
  end
end

The above test case for the MoviesController has an 'it' block, which has a string defining what the test is supposed to check. A do-end block to that 'it' has the actual test code.

The line Movie.should_receive(:find_in_tmdb).with('hardware') creates an expectation that the Movie class should receive the find_in_tmdb method call with a particular argument. An assumption is made here, that the user has actually filled in hardware in the search_terms box on the page that says Search for TMDb.

Once the expectation is setup, we simulate the post. 'post' is an RSpec extension (that is a part of [http://rubydoc.info/gems/rspec-rails/frames RSpec Rails)

References

<references/>

See Also

Definition

References

Books