CSC/ECE 517 Fall 2010/ch1 1f ap

From Expertiza_Wiki
Jump to navigation Jump to search

Introduction

Software Development demands quality like never before, simply because it is touching everything you can think of. Quality is an important aspect and there are various methodologies that help in achieving the same. Unit testing is one such methodology which has gained popularity over time.Suppose you want to move into a new house, the natural thing would be to check whether the taps, shower, grinder, exhaust fan etc are all functioning. What you just did was Unit testing, in simple terms it is identifying the smallest independent part of the system and making sure it works as it is suppose to.


Unit testing might be visual, manual but the real benefit is having it automated. There are well defined frameworks which actually make writing unit tests much simpler and these are known as Unit Testing Frameworks. Most of the programming languages have unit testing frameworks and you can find the list here. Ruby is a programming language with various features, but its dynamic which adds a lot of strengths but also can lead to a lot of surprises, as it is all at run-time. Testing is an imperative part and at the end of this chapter you will learn about the various available Unit Testing Frameworks for Ruby , a deep dive into few of the frameworks and a comparison which can help you in choosing the framework you want to adopt.


If you think software makes life easier, testing makes life of software much easier

Benefits of unit test frameworks

  • It provides us utilities and helps us develop test cases with ease
  • Easy to build, compile and run the test cases.
  • It helps us in isolating development code and test cases.
  • More important it helps us in testing the parts of the application and thus reducing the effort required in integration testing.
  • Some frameworks makes us to follow Test Driven Development" or Behavior Driven Development paradigm and helps us to achieve good design of the software.

Unit testing frameworks for Ruby

Test::Unit

Ruby has built in testing framework known as Test::Unit, it has all the expected capabilities and you have to just follow a few simple but essential guidelines to write your first Unit Test. Let’s define a simple class Positive.rb.

class Positive
 
  #A method which checks whether a number is Positive 

  def isPositive( n )
    
      if (n > 0)then
         return true
      end
      
  end
  
end 

Lets write another piece of code whose sole purpose is it to validate whether the Class Positive is actually working fine using Unit::Test framework .

require 'Positive'
require 'test/unit'

class TestPositive < Test::Unit::TestCase
  
 	 def test_ifPositive

   	 	assert(true,”@Positive.new().isPositive(5)”)
    	 	assert(true,”@Posivie.new().isPositive(100)”)

  	end
  
end
After running the test we get: 

Started
.
Finished in 0.001000 seconds.

1 tests, 2 assertions, 0 failures, 0 errors, 0 skips

Test run options: --seed 20322


A few important things to note :

  • We need “test/unit” library , hence Require ‘test/unit’ and the test class must inherit from Test::Unit::TestCase
  • All the methods in this must be starting with “test_”.
  • Assertions are the Key to tests as your result is based on these , you can find the available set here.

Now although the test is passing for the trivial cases, we have to test for many scenarios like, what happens if we pass a negative number, an invalid input or real number and many that you can think of. As we start adding these, the list of tests keeps growing and hence we have a notion of Test Suite, which simply is a single set of various tests and helps in running all the related tests at one go. Generally we have to initialize or run some code before and after we run our tests. The framework provides methods, setup and teardown to do the same.

Gathering the basics, we can have a general template for writing Test Cases and few of the things are mandatory

require 'THE CLASS THAT YOU WANT TO TEST'

require 'test/unit' # Mandatory 


class TestClassName < Test::Unit::TestCase  #All must Inherit this 
  
       # set up (initialize)
       def setup
             #optional
   	 end
  

       #Define you tests (Make use of the various assert statements)

 	 def test_name #all must start with “test”
                
   	      
       end

       #Tear Down 
       def teardown
	     #optional
  	end

  
end

This is a brief introduction to Test::Unit Framework, the biggest advantage being its availablity with base ruby library and an approach which suits Test Driven Development . Inheriting the base Test::Unit::TestCase class , setup , tear down , methods , assertions were some key aspects of this framework. Here are list of useful links for this framework.

Rspec

Rspec is one more unit test framework available for ruby programmers , it has following features.

  • This framework adopts Behaviour Driven development paradigm.
  • Test cases are written using domain specific language - Domain Specific Language (DSL) is a computer language that is targeted to a particular kind of problem, rather than a general purpose language that's aimed at any kind of software problem
  • RSpec framework provides us a way to write specifications for our code, that are executables. You will realize this once you start looking into the examples and start writing the test code.

Some keyword that you need to know before jumping into technical aspects about Rspec.

  • Expectations - These are assertion statments used inside the test case.
  • Example - This is a test method and it is wrapper around group of expectations.
  • Example Group - Collection of examples is called Example group and also known as "test case".

Lets take an example and explain on how to write a unit test case using Rspec framework.

    describe "Checking Account" do 
        it  "should have a balance greater than 0"  do 
             CheckingAccount = Account.new
             CheckingAccount account.balance.should > Money.new(0, :USD)
        end 
        it  “...”  do 
             //some expectations
        end 
    end 


Lets go through elements of this test case aka code example group.

1. it ()  :: This is called code example and it is similar to test method in Test::unit .This method takes string as an argument and the string describes the functionality/ behavior we are going to test about the system . it() method has a code block , which is collection of expectations. Expectations are enclosed between the do ... end keywords.

General structure of it() method:.

it  <String which summarizes the behaviour we are going to test in code block>
    do                                      
        Expectation1               <-- Text between do and end is called block and it has expectations..
        Expectation2                     aka assertions.     
        . 
        .
        Expectationn
   end


2. describe() :: We use describe() method to define example group aka test case in Test::Unit. This acts as an abstraction to many code examples i,e its a wrapper around multiple it() functions .

General structure of describe() construct looks like this.

describe <arbitrary no of input params>  
do                           <--- Start  of block , this encloses multiple code exampl
    //Here we will have multiple code examples i ,e it() functions, helper methods, before and each method
done


3. Methods We can have other methods apart from it() , inside the describe block

  1. Before and after methods.
  2. Helper methods.


1. Before and after methods.

  • before(:[each or all] ):Some times we need to run some code or do some setup prior to executing the code examples (test methods).We do this by using the before method, this gets executed first, prior to all code examples. It takes either :each or :all as parameter . If we use :each, then before method gets executed before running each code example. If we use :all, then before gets executed only once at the start, before running code examples.


  • after(:[each or all] ) : This is counterpart for before function. If we need to run some code or some clean up code ,post running the code examples, we use this function. It takes either :each or :all as parameter . If we use :each then after method gets executed after running each code example. If we use :all then after method gets executed only once, at the end after running all the code examples.


2. Helper methods:Sometimes we require code that is common across all the code examples.Instead of repeating the same code in each code example.we write helper method and this be utilized by all of the code examples present in the code example group.This method helps us to overcome duplication in the code.Even these helper methods can be used across the example groups.

module UserExampleHelpers

    def add_employee_details
        User.new(:email => 'e@mail.com' , :password => 'shhhhh' )
    end
    it  “Calculating payroll” do
        empx = add_employee_details
        # do more processing.
    end 
end


3.Nested code example groups(): To better organize our example group some times we need nesting of example groups and this feature is available in Rspec.


Expectations

Expectations are like assertions in Test::Unit framework and are most important part of any example. Expectation says at specific point in the execution of a code example, some thing should be in some state and they use language that is very easy to understand and even non programmer like project manager, can understand what we are doing in the given expectation.

Eg: result.should equal(10) It says the result should be equal to 10. Some more eg on expectations:-

  • result.should_not equal(10)
  • message.should match(/Its raining today//)
  • team.should have(2).players

To gain better understand of RSpec’s expectations, let’s get familiar with the different parts of the expectation. First we will start of with the should and should_not methods and will learn about different types of expression matchers.


1. should and should_not : Both of these methods are part of the Object Class(top most parent class in Ruby) , Both of these functions take Expression matcher or Ruby Expression with subset of operators available in Ruby as argument .Here expression matcher is an object as its name suggests it matches the expression.
Eg of Expression matchers.:
1)result.should equal(5) <-- expectation using should method .If the result turns out to be 5 then this expectation is successful.
2)result.should_not equal(5) <-- expectation using should_not method.If the result turns out to be 5 then this expectation fails.
To understand more about how ruby interprets this line ,follow the links in appendix section.

2. Matchers: Expression matcher is an object as its name suggests it matches the expression. There are several categories of matchers in Ruby.

  • Built-In Matchers :

Are the ones that gets shipped with Rspec.

1.result.should equal(2)                               ----> is equal(item) expression matcher
2.prime_numbers.should_not include(8)                  ----> is include(item) expression matcher.
3.(2 * 5).should == 10                                 ----> Testing for equality matcher.
4.result.should be_close(4.10, 0.004)                  ----> This matcher is used for floating point , this 
					                       says if the result is inbetween 4.10 plus or
                                                               minus 0.04 the expectation is success.          		             	                      						       
5.result.should match(/OOLS /)                         ----> This is a text matcher , and it succeeds if the 
                                                               result contains OOLS as part of the string							     
  • Predicate Matchers : A Ruby predicate matcher is one whose name ends with a “?” and returns a boolean response

eg: stack.should be_empty?

shoulda

Some prominent features about shoulda

  • shoulda is not a test framwork , it extends Ruby's existing Test::Unit framework with the idea of test contexts.Context is a section of test case,where test methods with similar characterstics are grouped.This feature of shoulda enables us to have single test case with multiple contexts, and each context can have its own environment.
  • one more interesting feature of shoulda is all the test scripts of Test::unit and Rspec are compatible with shoulda.
class StackTest < Test::Unit::TestCase
    context "Empty stack test" do
        setup do
            @newstack = stack(0);
        done
        
        should " Initial stack size should be zero" do
             assert_equal 0,@newstack.size		
        end
    end
end

More information on shoulda can be found from the following link shoulda

Comparison of Test frameworks

Test::Unit RSpec Shoulda
Test Approach Uses Test Driven Development ,software development methodology Uses Behavior Driven development, software development methodology Uses Test Driven development, software development methodology
Installation It comes as a built in package / gem / library with Ruby. Separate gem package named Rspec needs to be installed . Separate gem package named shoulda needs to be installed .
Core Features Test unit uses assertions, test methods and test suites to write the test case. Rspec uses describe , it , helper methods and expectations to write the test case. Shoulda uses context , should methods to write a test case.
Pre-Post Conditions Uses setup/teardown functions to do pre and post processing before and after running test case. Uses before/after functions to do pre and post processing before and after running test case. Uses setup/teardown functions to do pre and post processing before and after running test case.
Migration Difficult to migrate from an existing framework Difficult to migrate from an existing framework Compatible with Test::unit & RSpec and other frameworks

Conclusion

The Test Driven Development , Behavior Driven Development are all evolving and each programming language brings in its own flavor .Ruby has built in test framework test::unit and few other popular frameworks RSpec , Shoulda which provide a peek into the plethora of available frameworks and their strengths. Cucumber , Factory Girl are few of the recent additions.

The frameworks are quite stable and there is no down side to it unless a lot of time and resource is spent on automation , as it holds the key.It is rather hard to suggest a particular framework , although the ones introduced here are quite popular.

"The testing frameworks must be tested too "

References

  • Alameda, Eldon. Foundation Rails 2. 1 ed. Tokyo: Friends Of Ed, 2008. Print.
  • Astels, Dave, David Chelimsky, Zach Dennis, Aslak Hellesoy, Bryan Helmkamp, and Dan North. The RSpec Book: Behaviour Driven Development with RSpec, Cucumber, and Friends. NY: Pragmatic Bookshelf, 2009. Print.
  • Fowler, Chad, Andy Hunt, and Dave Thomas. Programming Ruby: The Pragmatic Programmers' Guide, Second Edition. 2nd ed. NY: Pragmatic Bookshelf, 2004. Print.
  • Ullman, Larry. Ruby: Visual QuickStart Guide. 1 ed. Berkeley, CA: Peachpit Press, 2008. Print.

External links