Skip to content
trans edited this page Jul 31, 2011 · 4 revisions

Some thoughts on how to handle assertions

There are two ways to handle assertions.

Most Ruby test frameworks raise a special error when an assertion fails. For instance, that exception class is Test::Unit::AssertionFailedError for Test::Unit, MiniTest::Assertion for MiniTest and AE::Assertion for the Assertive Expressive assertions framework. For these systems to support Ruby Test they only need to add an instance method, #assertion? to their respective classes.

def assertion?
  true
end

By adding this method, Ruby Test will be able to recognize the exception as an assertion error, as opposed to an another type of error.

This approach works well, making it easy for a wide variety of test frameworks to be supported by Ruby Test. However it does have two minor short-comings. Raising an assertion cuts off any following assertions within the same unit test from being executed and it prevents any universal tally of assertions failed or passed.

When Ruby Test invokes a unit test, it rescues all exceptions and adds them to a set of global lists. In the case of failed assertion that is the $TEST_ASSERTIONS variable.

  begin
    unit.call
  rescue Exception => error
    if error.assertion?
      $TEST_ASSERTIONS << error

This indicates the alternative approach a test framework can take to handle assertions. Rather than raise an assertion error, it simply needs to add a compatible assertion object onto the #TEST_ASSERTIONS list. A compatible object is simply an Exception instance that responds to #assertion?, but also #failed?. Ruby Test provides a class for this purpose. Here is a simple example of how a test framework could make use of it.

  def assert(message=nil, &block)
    if block.call
      $TEST_ASSERTIONS << Assertion.new(message)
    else
      $TEST_ASSERTIONS << Assertion.new(message, true)
    end
  end

Notice the second argument to new which marks the assertion as failed.

Clone this wiki locally