The current assert_raise method tests that a specific list of exceptions is raised. This is often overly restrictive,
because sometimes one has to allow for the raising of a base exception OR any exception derived from that
base exception. This patch adds that functionality by providing an optional last parameter to assert_raise. I did this
to preserve DRY and to try to stay backward-compatible. Older test suites should run unchanged. However, newer test
suites that include the optional parameter will not run correctly with older test-unit code. This would be a problem
even if I added a new method.
The last (optional) parameter must be a hash of hashes. The primary hash key must be the symbol :opts. The hash corresponding
to that key must contain the entry :allow_subclasses => true. Any other case will revert to the current, more restrictive
behavior. If the last parameter is the correct type (hash of hashes) and contains the correct key (:opts), it will be
removed from the argument list during assert_raise processing. This will somewhat future-proof the code so that additional
parameters could be added if necessary.
I patched the trunk version of assertions.rb, but could not get the trunk to compile, so I copied assertions.rb to my
1.85 installation and tested it there with existing unit tests. It seems to work fine.
Example:
def test_for_nil_ruleset
opts = {:opts => { :allow_subclasses => true }}
assert_raise Validators::RuleException, opts do
validator = Validators::ParamValidation.new(nil)
end
end
Here's the CHANGELOG entry, if you need one:
Here's the patch to the trunk version (revision 11764) of assertions.rb:
@@ -98,17 +98,39 @@
modules.any? {|mod| actual_exception.is_a?(mod)}
end
+ def _expected_exception_or_subclass?(actual_exception, exceptions, modules) # :nodoc:
+ exceptions.any? {|cls| actual_exception.class <= cls} or
+ modules.any? {|mod| actual_exception.is_a?(mod)}
+ end
+
##
# Passes if the block raises one of the given exceptions.
+ # If last argument is a hash with key of :opts, and value
+ # another hash {<some_sym> => true}, options will be
+ # processed. Currently there's only one option
+ # :allow_subclasses, which will make this assertion pass if
+ # the block raises one of the given exceptions <b>or
+ # any of their subclasses</b>.
#
- # Example:
+ # Examples:
# assert_raise RuntimeError, LoadError do
# raise 'Boom!!!'
# end
+ #
+ # opts = {:opts => {:allow_subclasses => true}}
+ # assert_raise RuntimeError, LoadError, opts do
+ # raise 'Boom!!!'
+ # end
public
def assert_raise(*args)
_wrap_assertion do
+ allow_subclasses = false
+ if Hash === (opts = args.last) and opts.has_key?(:opts)
+ args.pop
+ allow_subclasses = (Hash === opts[:opts] and opts[:opts][:allow_subclasses])
+ end
+
if Module === args.last
message = ""
else
@@ -127,7 +149,13 @@
false
end
full_message = build_message(message, "<?> exception expected but was\n?", expected,
actual_exception)
- assert_block(full_message) {_expected_exception?(actual_exception, exceptions, modules)}
+ assert_block(full_message) do
+ if allow_subclasses
+ _expected_exception_or_subclass?(actual_exception, exceptions, modules)
+ else
+ _expected_exception?(actual_exception, exceptions, modules)
+ end
+ end
actual_exception
end
end
Regards,
Edwin Fine |