Python unit test for a function that has try/except - python

I have a function that has try/except as follows:
def func_A():
try:
# do some stuff
except Exception as e:
log.error("there was an exception %s", str(e))
I want to write a unit test for this func_A()
More importantly, I want to ensure that
No exception was caught inside A
I have try/except just for safety.
Unless there is a bug, there should be no exception thrown inside A (although it will be caught with try/except) and that's what I want to validate with my unit test.
What is the best way for unit test to catch the case where there was an exception thrown and caught?

If you really need this, one possible way is to mock out the log.error object. After invoking the func_A function, you can make an assertion that your mock wasn't called.
Note that you should not catch exceptions at all if you don't intend to actually handle them. For proper test coverage, you should provide 2 tests here - one which checks each branching of the try/except.

Another possible solution is to split implementation into two functions:
Function foo() with logic without try statement. This way you can make sure that no exception is thrown in your implementation.
safe_foo() which wraps foo() into try statement. Then you can mock foo() to simulate throwing an exception by it and make sure every exception is caught.
Drawback is that either foo() will be part of a public interface or you will write tests for a private function.

You can have one variable which will track function executed properly or ended in exception.
def func_A():
function_state = True
try:
# do some stuff
except Exception as e:
log.error("there was an exception %s", str(e))
function_state = False
return function_state
Use assertTrue to validate function_state.

Option 1: Don't. This is testing an implementation detail. Try to write your test suite so that you very the function does everything you need it to do. If it does what you want with the inputs you want, you're good.
Option 2: You can modify the function to take a logger as a parameter. Then in the test case, pass in a mock object and check that the logging method gets called.

Related

Should the try/except be placed in the function declaration or call?

Both these snippets do the same thing:
Try/except in function declaration:
def something():
try:
# code goes here
except:
print("Error")
sys.exit(1)
something()
Try/except in function call:
def something():
# code goes here
try:
something()
except:
print("Error")
sys.exit(1)
Is there one that is better/more Pythonic/recommended by PEP8 or is it just up to personal preference? I understand that the second method would get tedious and repetitive if the function needs to be called more than once, but assuming the function is only called once, which one should I use?
the general rule is "only catch exceptions you can handle", see here for an explanation
note that an uncaught exception (in most languages) will cause the program to exit with an unsuccessful status code (i.e. your sys.exit(1)), it will probably also print out a message saying that an exception occurred. your demo therefore is emulating default behaviour, but doing it worse
further, you're catching every exception and this is generally bad style, e.g. you'll implicitly catch SystemExit and other internal exceptions that you probably shouldn't be dealing interacting with

Either/or assertions unittest

In a unittest I want to do something like:
result = myFunction()
self.assertFalse(result) OR self.assertRaises(Exception, myFunction)
with the idea being that if the implementation of myFunction changes to return false instead of raise an exception (or vice-versa), the test will still behave correctly (i.e. indicate a non-positive result).
Is something like this possible?
You can check if myFunction() raises a specific exception:
try:
self.assertFalse(myFunction())
except SpecificException:
pass
In this case if myFunction() raises SpecificException it would be silently ignored.
In other words, you would see the test failing in two cases: either myFunction() raises the exception you are not waiting for, or the result of myFunction() call doesn't evaluate to False.
In case if your function can throw any exception, you can check whether you caught an AssertionError in the except block and reraise it, silently ignoring other exceptions:
try:
self.assertFalse(myFunction())
except AssertionError:
raise
except:
pass
Note that this doesn't seem very intuitive and clear to me. In an ideal world, you would need to handle these two situations separately: create a test that would make myFunction() result into False and another one that would make it raise an exception.
You could, but that will make your test weaker. Every other unit test that uses myFunction will have to assume that it could either raise an exception or return False -- or else that test may break when the behavior changes.
Making all your other tests accomodate this uncertainty will make all those pieces of code harder to write. And for what purpose? The benefit is dubious.
Instead, make a definite test which uses assertRaises. Let the test fail if the implementation changes, and change the test then to assertFalse. That change may also cause other tests to fail, and that is a good thing. It will force you to review use cases for myFunction, and the repercussions for changing the behavior.

Python: Try/Except around whole module

I have a python module containing functions and a few classes. This module is basically used as a tool-set by several of my co-workers.
I want to set-up a sort of bug reporting system where anytime someone generates an exception that I don't handle, an email will be sent with information on the exception. This way I can continually improve the robustness of my code and the help-fullness of my own error messages. Is the best way to do this to just put a try/except block around the entire module?
There are several reasons I think your approach might not be the best.
Sometimes exceptions should be thrown. For example, if I pass some stupid argument to a function, it should complain by throwing an exception. You don't want to get an email every time someone passes a string instead of an integer, etc. do you?
Besides, wrapping the entire thing in a try...except won't work, as that will only be catching exceptions that would occur during the definition of the classes/functions (when your module is loaded/imported). For example,
# Your python library
try:
def foo():
raise Exception('foo exception')
return 42
except Exception as e:
print 'Handled: ', e
# A consumer of your library
foo()
The exception is still uncaught.
I guess you can make your own SelfMailingException and subclass it. Not that I would recommend this approach.
another option:
def raises(*exception_list):
def wrap(f):
def wrapped_f(*x, **y):
try:
f(*x, **y)
except Exception as e:
if not isinstance(e, tuple(exception_list)):
print('send mail')
# send mail
raise
return wrapped_f
return wrap
usage:
#raises(MyException)
def foo():
...

Is it possible to test exception conditions using Ludibrio?

I've recently started using Ludibrio for mocking objects in unit testing. So far it seems to be pretty streamlined, but I seem to have hit a snag when testing some failure scenarios and can't seem to find a solution online.
Some of the method calls I'm working with raise exceptions which I want to trap. So I want my mock object to simulate these conditions by raising an exception on a particular call. I tried doing it like this:
from ludibrio import *
with Mock() as myMock:
def raiseException():
raise Exception('blah')
myMock.test() >> raiseException()
try:
print myMock.test()
except Exception, e:
print 'Error: %s' % e
myMock.validate()
The trouble is, raiseException() is evaluated when the mock object is built, rather than when myMock.test() is called. So clearly this isn't the correct way to do this.
Is there a way to get the mock object to raise an exception at runtime? Or would the exception be intercepted as a failure and not get outside of the mock object anyway?
Further Googling has eventually yielded the answer. Simply tell the mock object to pass back an exception. This appears to then be raised on the outside:
myMock.test() >> Exception('blah')

Pass a Python unittest if an exception isn't raised

In the Python unittest framework, is there a way to pass a unit test if an exception wasn't raised, and fail with an AssertRaise otherwise?
If I understand your question correctly, you could do something like this:
def test_does_not_raise_on_valid_input(self):
raised = False
try:
do_something(42)
except:
raised = True
self.assertFalse(raised, 'Exception raised')
...assuming that you have a corresponding test that the correct Exception gets raised on invalid input, of course:
def test_does_raise_on_invalid_input(self):
self.assertRaises(OutOfCheese, do_something, 43)
However, as pointed out in the comments, you need to consider what it is that you are actually testing. It's likely that a test like...
def test_what_is_42(self):
self.assertEquals(do_something(42), 'Meaning of life')
...is better because it tests the desired behaviour of the system and will fail if an exception is raised.
Many of the comments on this page treat errors and failures as equivalent, which they are not. The right solution in my opinion is to explicitly fail the test if the exception is raised. E.g.:
def test_does_not_error(self):
try:
code_under_test()
except ThatException:
self.fail("code_under_test raised ThatException")
Simply call your functionality, e.g. do_something(). If an unhandled exception gets raised, the test automatically fails! There is really no reason to do anything else. This is also the reason why assertDoesNotRaise() does not exist.
Credit: comment by Sven

Categories

Resources