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.
-
I need to create a test which asserts that the boolean is not false or nil
Lets call ittest_assert_boolean_is_not_false_or_nil -
I would like to assert boolean which confirms my understanding of how to call methods in Ruby
Lets call assert with and without ()’s -
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 -
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:
-
I need to create a test which asserts that the boolean is false or nil
Let’s call ittest_assert_boolean_is_false_or_nil -
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. -
I would like to capture an expected raised exception when my block is executed
Sinceyield“yields” to the passed in block, lets wrap theyieldwith begin..rescue..end syntax. Next, we’ll verify that the class of the exception raised is the one that we expected. -
I would like to raise an exception if assert does not raise its own
For simplicity, lets add a flag to indicate ifassertraised 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
Ross Niemi's Musings