Skip to content

Commit

Permalink
Fix assertRaises when the exception has a metaclass
Browse files Browse the repository at this point in the history
Due to not taking into account that the type of the class can be a
subclass of type (which is the case when using metaclasses),
assertRaises would report a false positive in case an exception that was
expected had a custom metaclass.

This patch introduces a more robust checking if a given object is a
class or an instance of one, and adds a test case, to make sure
exceptions with custom metaclasses don't break assertRaises.
  • Loading branch information
djipko committed Mar 26, 2013
1 parent 877058e commit 54751af
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 1 deletion.
1 change: 1 addition & 0 deletions LICENSE
Expand Up @@ -17,6 +17,7 @@ The testtools authors are:
* Gavin Panella
* Martin Pool
* Vincent Ladeuil
* Nikola Đipanov

and are collectively referred to as "testtools developers".

Expand Down
2 changes: 2 additions & 0 deletions NEWS
Expand Up @@ -13,6 +13,8 @@ experimental and we might need to break it.

Improvements
------------
* ``assertRaises`` works properly for exception classes that have custom
metaclasses

* ``ConcurrentTestSuite`` was silently eating exceptions that propagate from
the test.run(result) method call. Ignoring them is fine in a normal test
Expand Down
4 changes: 3 additions & 1 deletion testtools/matchers/_exception.py
Expand Up @@ -44,7 +44,9 @@ def __init__(self, exception, value_re=None):
if istext(value_re):
value_re = AfterPreproccessing(str, MatchesRegex(value_re), False)
self.value_re = value_re
self._is_instance = type(self.expected) not in classtypes() + (tuple,)
expected_type = type(self.expected)
self._is_instance = not any(issubclass(expected_type, class_type)
for class_type in classtypes() + (tuple,))

def match(self, other):
if type(other) != tuple:
Expand Down
13 changes: 13 additions & 0 deletions testtools/tests/test_testcase.py
Expand Up @@ -302,6 +302,19 @@ def test_assertRaises(self):
# assertRaises asserts that a callable raises a particular exception.
self.assertRaises(RuntimeError, self.raiseError, RuntimeError)

def test_assertRaises_exception_w_metaclass(self):
# assertRaises works when called for exceptions with custom metaclasses
class MyExMeta(type):
def __init__(cls, name, bases, dct):
""" Do some dummy metaclass stuff """
dct.update({'answer': 42})
type.__init__(cls, name, bases, dct)

class MyEx(Exception):
__metaclass__ = MyExMeta

self.assertRaises(MyEx, self.raiseError, MyEx)

def test_assertRaises_fails_when_no_error_raised(self):
# assertRaises raises self.failureException when it's passed a
# callable that raises no error.
Expand Down

0 comments on commit 54751af

Please sign in to comment.