Too Cool for Internet Explorer

Monday, February 18, 2008

Ruby from Test::Unit to RSpec part 2 of 3

Now that we know about unit tests, we can go a step further. TDD (test driven development). But what is that on the first place ? Different from Unit tests, in TDD, tests always comes first.

The best definition on TDD basic idea comes from Manfred Lange, one of the csUnit Lead Developers:

"Test a little, code a little, refactor a little -- in that sequence! And the rhythm here is not in hours or days. The rhythm for the entire sequence is seconds or minutes. Run tests frequently, preferably as often as possible. With TDD the test always comes first, while with 'unit testing' in the broadest sense this is not clear. Generating tests after-the-fact is not considered to be TDD. Sometimes I even prefer the term 'Test-Driven Design' as it makes clearer what this really is about."

When we talk about TDD, we are not talking just about tests. We are talking about a development process, consisting on:

  1. Create a test code
  2. See the test fail
  3. Create the code to make the test pass.
  4. Refactoring the code
  5. Refactoring the test
  6. Do it again

The principal benefit on TDD design is that it will make OO design goals easy to achieve: TDD is a technique for determining class structure by making testability a first class consideration in your design. Focusing on testing a unit of code at a time leads to creating cohesive classes with a distinct purpose and responsibility. The need and desire to quickly setup an isolated unit test on a class will lead to a loosely coupled design.

The use of UML and CRC, is based on the belief that it is easy to design ideas on abstract artifacts, because code is difficult to change once written. This assumption could be changed because TDD resulting code is cohesive and loosely coupled. With a couple of good tools automating the tests and assembling the code, can make this technique much more efficient.

There is an interesting (but incomplete) series of articles on Jeremy D Miller's blog about a set of “TDD laws”.

Coming from Kent Beck's “Test-Driven Development By Example” book, and adapted to Ruby by Rafael C. Schouery, here is a sample on how to create a Fibonacci number function recursively using TDD.

First, we would like that fib(0) = 0. So, we start to write down our test.

require 'test/unit'

class TestFibonacci <>

To get the test pass, we need a Fibonacci class and a self.fib method on it.

class Fibonacci
def self.fib(n)
return 0;
end
end

With the tests going on, we could tests if fib(1) = 1, so we change the test_fib method to include this condition.

def test_fib
assert_equal(0,Fibonacci.fib(0));
assert_equal(1,Fibonacci.fib(1));
end

Which obviously will not pass. We can change the self.fib to the following.

def self.fib(n)
return 0 if(n == 0)
return 1
end

Now the tests are passing, but there is a code repetition, a bad practice. Refactoring the code with the DRY principle, we have:

def test_fib0
cases = [[0,0],[1,1]]
cases.each do |c|
assert_equal(c[1],Fibonacci.fib(c[0]))
end
end

Now we have pair tests to try (n,fib(n)). We can add the pair (2,1) and the tests still pass. If we add the pair (3,2) the test will fail. we can fix the code to fix this problem.

def self.fib(n)
return 0 if(n == 0)
return 1 if(n <= 2) return 2 end

Now we have the tests passing, we can make a more generic code, and instead of 2 we can return:

fib(n-1) + fib(n-2)

And now we have the final code:

require 'test/unit'
class TestFibonacci < cases =" [[0,0],[1,1],[2,1],[3,2]]" n ="="">


That is it TDD.

Sunday, February 10, 2008

Ruby from Test::Unit to RSpec part 1 of 3

What is this all about ? Coding tests for applications ? Why ?

The technical view:

  • Avoid creating a bug when fixing another one.
  • Don't use to play with the program to find errors (it is slow).
  • Unit tests are the first step on TDD (test driven development).

The programmer view:
  • I will not write tests, that is what testers do.
  • Writing tests is a waste of time, it slows down my productivity.
  • My code works, I'm a good programmer, and I don't need to write tests to prove it.

The tester view:
  • Programmers couldn't write tests, that is what testers do.
  • If programmers write tests where our jobs will go ?
  • If you ask a programmer to write tests you know nothing about tests.
Unit tests before code, advantages:

Creating unit tests before the program solution, developers will consider what need to be done. Requirements become stronger when supported by tests. Couldn't be misunderstanding on a specification written in the form of executable code.

The feedback is immediate while the work goes on. Creating unit tests first, developers can feel when all necessary conditions and functionality have being considered and implemented.

There is also a benefit to system design. Creating tests first your software design will be influenced by a desire to test everything valuable to the customer, reflecting on an easier to test design.

How to create a Unit test ?

  • Require 'test/unit' in your test script.
  • Create a class that subclasses Test::Unit::TestCase.
  • Add a method that begins with "test" to your class.
  • Make assertions in your test method.
  • Optionally define #setup and/or #teardown to set up and/or tear down your common test fixture.
Comes from Google an example from the Ruby/Password library's unit tests.

require 'test/unit'
require 'password'

class TC_PasswordTest < Test::Unit::TestCase

def test_check
# Check for a weak password.
pw = Password.new( 'foo' )
assert_raises( Password::WeakPassword ) { pw.check }

# Check for a good password.
pw = Password.new( 'G@7flAxg' )
assert_nothing_raised { pw.check }

# Check for an exception on bad dictionary path.
assert_raises( Password::DictionaryError ) { pw.check( '/tmp/nodict' ) }
end

end

This is just a basic sample, but that is the idea for now.

You can search for available Ruby assertions on Ruby-Doc.

Test Case is a class that inherits from Test::Unit::TestCase, and contains a "testing strategy", comprised of contextually related tests, and each test is a method named test_something.

Test Suite is a collection of test cases. When a test suite is executed, each test belonging to it is executed. A suite is excellent for continuous-build integration for example. Tests could be aggregated on a suite when needed.

Test Fixture is a way to group "setup" and "teardown" methods, to prepare your test unit framework to prepare the environment to your tests.

Test Harness is a collection of software and test data configured to run unit tests under varying conditions monitoring its bahavior and outputs.

Assertions are the heart of unit testing, providing a way to define expected results for a test. The goal is to test the behavior, by setting the test to pass or fail according to an expected type and value.

Mock Objects mimics behavior of real objects on controlled environments, programmers can use these objects to meet the interface requirements on complex real functionalities, without calling complex underlying or collaborating classes.

Test Unit and command line options:

To see all options just call a test with the -h option.

ruby my_test.rb -h

The best options to try are:

-r which brings up a GUI interface.
-n and -t which specifies specific methods and classes to be tested

More on command line options in this Scott Patten's article.

Wednesday, February 6, 2008

Ruby duck typing effect


Last week we could see questions about to_s and to_str on the forum.

Some assumptions too:

  • Appears to be to_str an alias for to_s
  • Most classes implement to_s, just a few implement to_str.
  • There existences just confirm Ruby immaturity.
  • Some are automatic casting others are a "do it yourself" version.

So why are these to_str, to_int, to_ary methods there ?

There is a pragmatic example to help us with Roman numbers.

A Roman number is a representation of an integer, it is not a number it self, but has an integer behavior. So, for practical purposes, if we have a Roman number object, acting like an integer one.

To achieve that, Ruby has the "conversion protocols" concept, which means, an object may convert itself to an object of another class, and there are three ways to do that.

  1. Non strict methods like to_s and to_i. They may be applied on objects that are just near the desired behavior. On the Roman number example, we probably need the to_s method to get a string representation of a number.
  2. Strict methods like to_str and to_int. That are only used on objects that naturally have this kind of behavior. On the Roman number example, they are clearly representing integers, so, they should implement to_int.
  3. Numeric coercion, which let methods deal with different numeric parameters, based on the coerce method. It takes two numbers (a receiver, and a parameter), it returns a two-element array with representations of these numbers, but with parameter on the first element and receiver on the second. These two array elements are objects of the same class, so, therefore they can be added, subtracted, multiplied, compared, etc.

That is an evolving process, for example: the Pathname object responds different to to_str method on Ruby 1.8.6 and 1.9.0. It was considered a regular string behavior in the past, not any more.

A code to think:

require 'pathname'
path = Pathname.new("/tmp/myfile")
puts '================ strict methods'
name = path.to_s # not needed on 1.8.6
puts path.class
puts path.respond_to?(:to_str)
puts path.respond_to?(:to_s)
heading = "File name is " + name
puts heading
heading = "File name is " + path
puts heading # will not work on 1.9.0
puts '================ non strict methods'
def evaluate(parm_object)
if parm_object.respond_to?(:to_str)
puts parm_object + '2'
else
puts parm_object + 2
end
end
evaluate('2')
evaluate(2)
puts '================ numeric coersion'
puts (3.8).coerce(9).inspect
puts 8.coerce(3.5).inspect
puts (7.2).coerce(4.1).inspect
puts 4.coerce(7).inspect
puts '================ end of examples'