I want to write a test to establish that an Exception is not raised in a given circumstance.
It\'s straightforward to test if an Exception is raise
def _assertNotRaises(self, exception, obj, attr):
try:
result = getattr(obj, attr)
if hasattr(result, '__call__'):
result()
except Exception as e:
if isinstance(e, exception):
raise AssertionError('{}.{} raises {}.'.format(obj, attr, exception))
could be modified if you need to accept parameters.
call like
self._assertNotRaises(IndexError, array, 'sort')
Hi - I want to write a test to establish that an Exception is not raised in a given circumstance.
That's the default assumption -- exceptions are not raised.
If you say nothing else, that's assumed in every single test.
You don't have to actually write an any assertion for that.
You can define assertNotRaises
by reusing about 90% of the original implementation of assertRaises
in the unittest
module. With this approach, you end up with an assertNotRaises
method that, aside from its reversed failure condition, behaves identically to assertRaises
.
It turns out to be surprisingly easy to add an assertNotRaises
method to unittest.TestCase
(it took me about 4 times as long to write this answer as it did the code). Here's a live demo of the assertNotRaises method in action. Just like assertRaises, you can either pass a callable and args to assertNotRaises
, or you can use it in a with
statement. The live demo includes a test cases that demonstrates that assertNotRaises
works as intended.
The implementation of assertRaises
in unittest
is fairly complicated, but with a little bit of clever subclassing you can override and reverse its failure condition.
assertRaises
is a short method that basically just creates an instance of the unittest.case._AssertRaisesContext
class and returns it (see its definition in the unittest.case
module). You can define your own _AssertNotRaisesContext
class by subclassing _AssertRaisesContext
and overriding its __exit__
method:
import traceback
from unittest.case import _AssertRaisesContext
class _AssertNotRaisesContext(_AssertRaisesContext):
def __exit__(self, exc_type, exc_value, tb):
if exc_type is not None:
self.exception = exc_value.with_traceback(None)
try:
exc_name = self.expected.__name__
except AttributeError:
exc_name = str(self.expected)
if self.obj_name:
self._raiseFailure("{} raised by {}".format(exc_name,
self.obj_name))
else:
self._raiseFailure("{} raised".format(exc_name))
else:
traceback.clear_frames(tb)
return True
Normally you define test case classes by having them inherit from TestCase
. If you instead inherit from a subclass MyTestCase
:
class MyTestCase(unittest.TestCase):
def assertNotRaises(self, expected_exception, *args, **kwargs):
context = _AssertNotRaisesContext(expected_exception, self)
try:
return context.handle('assertNotRaises', args, kwargs)
finally:
context = None
all of your test cases will now have the assertNotRaises
method available to them.
If you pass an Exception class to assertRaises()
, a context manager is provided. This can improve the readability of your tests:
# raise exception if Application created with bad data
with self.assertRaises(pySourceAidExceptions.PathIsNotAValidOne):
application = Application("abcdef", "")
This allows you to test error cases in your code.
In this case, you are testing the PathIsNotAValidOne
is raised when you pass invalid parameters to the Application constructor.
def run_test(self):
try:
myFunc()
except ExceptionType:
self.fail("myFunc() raised ExceptionType unexpectedly!")
I am the original poster and I accepted the above answer by DGH without having first used it in the code.
Once I did use I realised that it needed a little tweaking to actually do what I needed it to do (to be fair to DGH he/she did say "or something similar" !).
I thought it was worth posting the tweak here for the benefit of others:
try:
a = Application("abcdef", "")
except pySourceAidExceptions.PathIsNotAValidOne:
pass
except:
self.assertTrue(False)
What I was attempting to do here was to ensure that if an attempt was made to instantiate an Application object with a second argument of spaces the pySourceAidExceptions.PathIsNotAValidOne would be raised.
I believe that using the above code (based heavily on DGH's answer) will do that.