430
votes

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 raised ...

sInvalidPath=AlwaysSuppliesAnInvalidPath()
self.assertRaises(PathIsNotAValidOne, MyObject, sInvalidPath) 

... but how can you do the opposite.

Something like this i what I'm after ...

sValidPath=AlwaysSuppliesAValidPath()
self.assertNotRaises(PathIsNotAValidOne, MyObject, sValidPath) 
10
You could always simply do whatever ought to work in the test. If it raises an error, that will show up (counted as an error, rather than a failure). Of course, that assumes that it doesn't raise any error, rather than just a defined type of error. Other than that, I guess you'd have to write your own.Thomas K
It turns out that you can in fact implement an assertNotRaises method that shares 90% of its code/behavior with assertRaises in about ~30-ish lines of code. See my answer below for details.tel
I want this so I can compare two functions with hypothesis to make sure they produce the same output for all kinds of input, while ignoring the cases where the original raises an exception. assume(func(a)) doesn't work because output can be an array with ambiguous truth value. So I just want to call a function and get True if it doesn't fail. assume(func(a) is not None) works I guessendolith
Does this answer your question? Pass a Python unittest if an exception isn't raisedFlimm

10 Answers

461
votes
def run_test(self):
    try:
        myFunc()
    except ExceptionType:
        self.fail("myFunc() raised ExceptionType unexpectedly!")
77
votes

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.

53
votes

Just call the function. If it raises an exception, the unit test framework will flag this as an error. You might like to add a comment, e.g.:

sValidPath=AlwaysSuppliesAValidPath()
# Check PathIsNotAValidOne not thrown
MyObject(sValidPath)
16
votes

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.

13
votes

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.

TLDR and live demo

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.

Details

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.

3
votes
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')
2
votes

I've found it useful to monkey-patch unittest as follows:

def assertMayRaise(self, exception, expr):
  if exception is None:
    try:
      expr()
    except:
      info = sys.exc_info()
      self.fail('%s raised' % repr(info[0]))
  else:
    self.assertRaises(exception, expr)

unittest.TestCase.assertMayRaise = assertMayRaise

This clarifies intent when testing for the absence of an exception:

self.assertMayRaise(None, does_not_raise)

This also simplifies testing in a loop, which I often find myself doing:

# ValueError is raised only for op(x,x), op(y,y) and op(z,z).
for i,(a,b) in enumerate(itertools.product([x,y,z], [x,y,z])):
  self.assertMayRaise(None if i%4 else ValueError, lambda: op(a, b))
1
votes

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.

1
votes

you can try like that. try: self.assertRaises(None,function,arg1, arg2) except: pass if you don't put code inside try block it will through exception' AssertionError: None not raised " and test case will be failed. Test case will be pass if put inside try block which is expected behaviour.

-1
votes

One straight forward way to ensure the object is initialized without any error is to test the object's type instance.

Here is an example :

p = SomeClass(param1=_param1_value)
self.assertTrue(isinstance(p, SomeClass))