How to assertRaises in unittest an exception caught in try except block? - python

In my production function:
def myfunction():
try:
do_stuff()
(...)
raise MyException("...")
except MyException as exception:
do_clean_up(exception)
My test fails, because the exception is caught in the try/except block
def test_raise(self):
with self.assertRaises(MyException):
myfunction()
self.assertRaises is never called.
How to guarantee that the exception is caught during testing?
The exception is never asserted
AssertionError: MyException not raised

This is because you caught the exception MyException direct in myFunction().
Comment out out the try-except clause and try again, test should pass.
assertRaises is used for uncaught errors. You can also re-raise in except block.

The exception is handled internally, so there is no external evidence that the exception is raised.
However, both MyException and do_clean_up are external names, which means you can patch them and make assertions about whether they do or do not get used. For example,
# Make sure the names you are patching are correct
with unittest.mock.patch('MyException', wraps=MyException) as mock_exc, \
unittest.mock.patch('do_clean_up', wraps=do_clean_up) as mock_cleanup:
myfunction()
if mock_exc.called:
mock_cleanup.assert_called()

Related

Could not catch raise for this case

I have a re-triable wrapper class in Python which helps other func to retry. Let's assume my class is just called Wrapper. Here's my rough implementation (not real code):
Wrapper:
while True:
try:
func()
except Exception as e:
pass
num_retry--
if num_try == 0
raise
When I use the wrapper, I do:
try:
// use Wrapper
except Exception as e:
// handle exception
But I found the except part doesn't capture the exception, why? Anything special in Python about raise?
The reason is because you already captured the exception previously when you indicated that it was ignored with the sentence pass into Wrapper. Then the exception will not go up to the first try since it will be ignored.
Remove the capture of the exepction into Wrapper, and only let the capture outside. But the correct is that you should capture the exception into Wrapper not outside of him.

How can I inspect a ValidationError exception while testing with django TestCase?

I'm working with Django testing using a django.test.TestCase and I would like to know the pythonic way to inspect a validation error. I've come up with the below, which works, but it feels a little long winded and prone to error over a large number of tests
self.valid_account.contact_number = "1234567" # Too short
with self.assertRaises(ValidationError):
try:
self.valid_account.full_clean()
self.fail("Did not raise validation error")
except ValidationError as e:
self.assertTrue('contact_number' in e.message_dict)
raise e
Is there a better way to inspect and test an exception?
There's no point in using assertRaises if you're manually handling the exception. If you need to inspect the exception too, the solution is documented:
The context manager will store the caught exception object in its exception attribute. This can be useful if the intention is to perform additional checks on the exception raised
So in your case this would look like:
self.valid_account.contact_number = "1234567" # Too short
with self.assertRaises(ValidationError) as cm:
self.valid_account.full_clean()
self.assertTrue('contact_number' in cm.exception.message_dict)

usage of except and store error in a variable

I need to catch all the errors,exceptions, and everything that stops the execution of a code and store it in a variable.
I want something like this:
try:
Error generating code
except as err:
print err
But this doesnt work. Is there any other way to do the same?
except as err: doesn't work because the correct syntax is:
except TypeOfError as somename:
To catch any type of error, use Exception as the type,
it is the common base class for all non-exit exceptions in Python:
try:
# Error generating code
except Exception as err:
print(err)
err will be an instance of the actual exception that was raised,
you can see its correct type with type(err),
and it's attributes and methods with dir(err).
Keep in mind that it's recommended to use the most specific type of exception that may be raised.
See more details in Python's tutorial on error handling.

How to get exception message in Python properly

What is the best way to get exceptions' messages from components of standard library in Python?
I noticed that in some cases you can get it via message field like this:
try:
pass
except Exception as ex:
print(ex.message)
but in some cases (for example, in case of socket errors) you have to do something like this:
try:
pass
except socket.error as ex:
print(ex)
I wondered is there any standard way to cover most of these situations?
If you look at the documentation for the built-in errors, you'll see that most Exception classes assign their first argument as a message attribute. Not all of them do though.
Notably,EnvironmentError (with subclasses IOError and OSError) has a first argument of errno, second of strerror. There is no message... strerror is roughly analogous to what would normally be a message.
More generally, subclasses of Exception can do whatever they want. They may or may not have a message attribute. Future built-in Exceptions may not have a message attribute. Any Exception subclass imported from third-party libraries or user code may not have a message attribute.
I think the proper way of handling this is to identify the specific Exception subclasses you want to catch, and then catch only those instead of everything with an except Exception, then utilize whatever attributes that specific subclass defines however you want.
If you must print something, I think that printing the caught Exception itself is most likely to do what you want, whether it has a message attribute or not.
You could also check for the message attribute if you wanted, like this, but I wouldn't really suggest it as it just seems messy:
try:
pass
except Exception as e:
# Just print(e) is cleaner and more likely what you want,
# but if you insist on printing message specifically whenever possible...
if hasattr(e, 'message'):
print(e.message)
else:
print(e)
To improve on the answer provided by #artofwarfare, here is what I consider a neater way to check for the message attribute and print it or print the Exception object as a fallback.
try:
pass
except Exception as e:
print getattr(e, 'message', repr(e))
The call to repr is optional, but I find it necessary in some use cases.
Update #1:
Following the comment by #MadPhysicist, here's a proof of why the call to repr might be necessary. Try running the following code in your interpreter:
try:
raise Exception
except Exception as e:
print(getattr(e, 'message', repr(e)))
print(getattr(e, 'message', str(e)))
The repr(e) line will print Exception() and the str(e) line will print an empty string.
Update #2:
Here is a demo with specifics for Python 2.7 and 3.5: https://gist.github.com/takwas/3b7a6edddef783f2abddffda1439f533
I too had the same problem. Digging into this I found that the Exception class has an args attribute, which captures the arguments that were used to create the exception. If you narrow the exceptions that except will catch to a subset, you should be able to determine how they were constructed, and thus which argument contains the message.
try:
# do something that may raise an AuthException
except AuthException as ex:
if ex.args[0] == "Authentication Timeout.":
# handle timeout
else:
# generic handling
from traceback import format_exc
try:
fault = 10/0
except ZeroDivision:
print(format_exc())
Another possibility is to use the format_exc() method from the traceback module.
I had the same problem. I think the best solution is to use log.exception, which will automatically print out stack trace and error message, such as:
try:
pass
log.info('Success')
except:
log.exception('Failed')

Exception handling in Python

http://docs.python.org/library/imaplib.html states that:
exception IMAP4.error
Exception raised on any errors. The reason for the exception is passed to the constructor as a string.
What does "exception is passed to the constructor as a string" mean? What would the code look like that can print the reason.
Just use print str(exception).
You can specify the reason when constructing the exception yourself, and put it into a variable when catching the exception.
try:
raise imaplib.IMAP4.error('Some exception')
except imaplib.IMAP4.error, error:
print error

Categories