Learning Ruby – TDD Style! (Part 2)

start | previous | next | finish

In order to do Test Driven Development (TDD), we need to become intimately familiar with assertions. In my last blog entry, we started to look at the assert method. Lets consider the contract of this method:

assert(boolean, message=nil)
– Asserts that boolean is not false or nil.

When testing this method I’m making the conscious decision not to test assert using the other methods that Test::Unit::Assertions offers. Although it’ll make things more difficult at first, it will force us to learn aspects of the Ruby language that we would not be able to see if we used one “assert” to test another “assert”.

So what should we test first? Well, lets try the happy path first: Let’s test to make sure assert does not fail if boolean is not false or nil.

require 'test/unit'

class AssertionsTest < Test::Unit::TestCase

  def test_assert_boolean_is_not_false_or_nil
    assert true
    assert(true)
    assert true, 'A message you will not see'
    assert true, "A message you will not see"
    assert true, nil
    assert true, ''
    assert true, ""
    assert true, String.new
    assert true, :something
    assert 'bogus'
  end
end

TDD in a pairing fashion is usually best experienced as the test was typed so that the reviewer can ask questions in real-time. In order to “simulate” this approach, the following bullets describe the thought process I took in order to write this test.

  1. I need to create a test which asserts that the boolean is not false or nil
    Lets call it test_assert_boolean_is_not_false_or_nil

  2. I would like to assert boolean which confirms my understanding of how to call methods in Ruby
    Lets call assert with and without ()’s

  3. I would like to assert boolean-message which confirms my understanding of how strings work in Ruby
    Lets call assert with a message that is a string with single and double quotes, nil, empty quotes, String.new, and a symbol

  4. I would like to assert boolean for a non-boolean, non-nil object
    Lets call assert with a “boolean” that is a string

Now when I run my test I see that it passes. If the Test::Unit::Assertions module did not exist and my goal was to develop assert using TDD, then my test would fail and I would proceed to implement the assert method. Once done, I would then re-run my test to ensure I implemented the method as per spec. If my test failed, I would go back to the assert method, fix it, and then re-run my test until it passes.

Since we’re testing the behavior of assert, we can now move on to the unhappy path test. Specifically, we are now going to write a test to ensure that an exception is raised when boolean is false or nil.

The easiest way to test this would have been to use the assert_block method, but since we are trying to learn Ruby we’ll pretend that it doesn’t exist and we will write our own.

  ASSERTION_FAILED = Test::Unit::AssertionFailedError

  def test_assert_boolean_is_false_or_nil
    my_assert_exception_raised(ASSERTION_FAILED) { assert false }
    my_assert_exception_raised(ASSERTION_FAILED) { assert nil }  
  end
  
  def my_assert_exception_raised expected
    begin
      yield
      exceptionNotRaised = true
    rescue => exception
      my_assert_type_of expected, exception      
    end
    if exceptionNotRaised
      raise Test::Unit::AssertionFailedError.new( expected + 
                " should have been raised.")
    end
  end
  
  def my_assert_type_of expectedClass, actualInstance
    my_assert_not_nil actualInstance
    my_assert_classes_are_equal nil, nil
    my_assert_classes_are_equal actualInstance.class, actualInstance.class
    my_assert_classes_are_equal expectedClass, expectedClass
    my_assert_classes_are_equal expectedClass, actualInstance.class
  end
  
  def my_assert_not_nil instance
    assert instance
    assert !instance.nil?
    assert instance != nil  
  end
  
  def my_assert_classes_are_equal clazz1, clazz2
    assert clazz1 == clazz2
    assert clazz1.eql?(clazz2)
    assert clazz1.equal?(clazz2)
  end

To distinguish Test::Unit::Assertions assertions from our custom assertion, I have prefixed our custom assertion with my_. As before, the following is the thought process taken:

  1. I need to create a test which asserts that the boolean is false or nil
    Let’s call it test_assert_boolean_is_false_or_nil

  2. I would like to make sure an exception is thrown when asserting false
    Since we can reuse this for the assert nil case, let’s create a method which we can pass a block into it. A block is a “nameless function” or just a list of instructions you want to execute.

  3. I would like to capture an expected raised exception when my block is executed
    Since yield “yields” to the passed in block, lets wrap the yield with begin..rescue..end syntax. Next, we’ll verify that the class of the exception raised is the one that we expected.

  4. I would like to raise an exception if assert does not raise its own
    For simplicity, lets add a flag to indicate if assert raised an exception. If not, our if block will raise its own.

In order to shorten this blog entry, I’ll let you take a look at the implementation for my_assert_type_of on your own. The code here is another example of proving out what we know of the Ruby language. Specifically, this code tests the behavior of:

  • nil checking an instance
  • nil class equality
  • non-nil class equality
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: