how to handle Exceptions in programs¶
This is a continuation of how to test that an Exception is raised
test_catching_exceptions_w_messages¶
I add a failing test to
test_exceptions.py
def test_catching_exceptions_w_messages(self): src.exceptions.raise_exception()
and the terminal shows AttributeError
AttributeError: module 'src.exceptions' has no attribute 'raise_exception'
green: make it pass¶
I add the name to
exceptions.py
def function_name(): return None raise_exception
and get NameError
NameError: name 'raise_exception' is not defined
I point it to None
raise_exception = None
and the terminal shows TypeError
TypeError: 'NoneType' object is not callable
When I make it a function
def raise_exception(): return None
the test passes
I want the function to raise an Exception when it is called
def test_catching_exceptions_w_messages(self): with self.assertRaises(Exception): src.exceptions.raise_exception()
the terminal shows AssertionError
AssertionError: Exception not raised
I use the raise statement
def raise_exception(): raise Exception
and the test is green
I can use the unittest.TestCase.assertRaisesRegex method to be more specific in tests, it checks that the code in its context raises the Exception it is given, with the message it is given, it uses Regular Expressions for this
def test_catching_exceptions_w_messages(self): with self.assertRaisesRegex( Exception, 'BOOM!' ): src.exceptions.raise_exception()
the terminal shows AssertionError
AssertionError: "BOOM!" does not match ""
the Exception is right, the message is not. I add it
def raise_exception(): raise Exception('BOOM!')
and the test passes. Time to add an how to test that an Exception is raised to the program
test_catching_failure¶
red: make it fail¶
I add a new failing test
def test_catching_failure(self):
self.assertEqual(
src.exceptions.an_exception_handler(
src.exceptions.raise_exception
),
'failed'
)
the terminal shows AttributeError
AttributeError: module 'src.exceptions' has no attribute 'an_exception_handler'
green: make it pass¶
When I add the name
def raise_exception(): raise Exception('BOOM') an_exception_handler
I get NameError
NameError: name 'an_exception_handler' is not defined
I point it to None
an_exception_handler = None
and get TypeError
TypeError: 'NoneType' object is not callable
then I make it a function
def an_exception_handler(): return None
and get a different message for the TypeError
TypeError: an_exception_handler() takes 0 positional arguments but 1 was given
I make the function take input
def an_exception_handler(argument): return None
and the terminal shows AssertionError
AssertionError: None != 'failed'
the result of calling
src.exceptions.an_exception_handler
is None, the test expects'failed'
I change the return statement to match the expectation
def an_exception_handler(argument): return 'failed'
and the test passes.
test_catching_success¶
I want an_exception_handler
to process its input and return failed
when an Exception happens or success
when it does not.
red: make it fail¶
I add a new test
def test_catching_success(self):
self.assertEqual(
src.exceptions.an_exception_handler(
src.exceptions.does_not_raise_exception
),
'succeeded'
)
and get AttributeError
AttributeError: module 'src.exceptions' has no attribute 'does_not_raise_exception'
green: make it pass¶
I add the name to
exceptions.py
def raise_exception(): raise Exception('BOOM') does_not_raise_exception def an_exception_handler(argument): return 'failed'
and the terminal shows NameError
NameError: name 'does_not_raise_exception' is not defined
I point it to None
does_not_raise_exception = None
and get AssertionError
AssertionError: 'failed' != 'succeeded'
src.exceptions.an_exception_handler
still returns'failed'
, the test expects'succeeded'
I make
an_exception_handler
return its inputdef an_exception_handler(argument): return argument return 'failed'
and the terminal shows AssertionError
FAILED tests/test_exceptions.py::TestExceptions::test_catching_failure - AssertionError: <function raise_exception at 0xabcd12e34567> != 'failed' FAILED tests/test_exceptions.py::TestExceptions::test_catching_success - AssertionError: None != 'succeeded'
I rename the input parameter to describe it better
def an_exception_handler(a_function): return a_function return 'failed'
then make
an_exception_handler
return the result of a call to its input as a functiondef an_exception_handler(a_function): return a_function() return 'failed'
and the terminal shows TypeError
a_function = None def an_exception_handler(a_function): > return a_function() E TypeError: 'NoneType' object is not callable
because
does_not_raise_exception
points to None, which is not callable. I make it a function to fix thisdef does_not_raise_exception(): return None
and the terminal shows AssertionError
AssertionError: None != 'succeeded'
the result of calling
src.exceptions.raise_exception
intest_catching_failure
is an Exception with a messageFAILED tests/test_exceptions.py::TestExceptions::test_catching_failure - Exception: 'BOOM!'
how to use try…except…else¶
when I add a try statement
def an_exception_handler(a_function): try: a_function() except Exception: return 'failed'
test_catching_failure
passes and the terminal still shows AssertionErrorAssertionError: None != 'succeeded'
I add an else clause
def an_exception_handler(a_function): try: a_function() except Exception: return 'failed' else: return None
then change its return statement
def an_exception_handler(a_function): try: a_function() except Exception: return 'failed' else: return 'succeeded'
and the terminal shows passing tests.
The try statement is used to catch/handle exceptions in Python. It allows the program to make a decision when it runs into an Exception. I think of it as
try
running thisexcept Exception
- when running this raisesException
, run the code in this blockelse
- when running this does NOT raiseException
, run the code in this block
In this case
try
callinga_function()
except Exception
- when callinga_function()
raisesException
return'failed'
else
- when callinga_function()
does NOT raiseException
return'succeeded'
I can be more specific with the Exception in the
except
block, for exampledef an_exception_handler(a_function): try: a_function() except ModuleNotFoundError: return 'failed' else: return 'succeeded'
shows this in the terminal
Exception: 'BOOM!'
because Exception is not ModuleNotFoundError, the try statement will only catch the Exception given in the
except
block and its children, all others will be raisedI change it back to what works
def an_exception_handler(a_function): try: a_function() except Exception: return 'failed' else: return 'succeeded'
and the terminal shows green again
review¶
I ran tests to show how to cause Exceptions, and catch or handle them in tests and programs. Would you like to test measuring sleep duration?