Decorator for Exception handling in Python - python

I have a situation when unittest method calls a helper method,
if there is a runtime error on the helper method,I wanted to raise sys.exit()
and exit the process without executing any further test case,As sys.exit() is being handled & silenced
by the unittest I had to to use os._exit()
But I don't want to do this for all exception ,only for specific Exceptions in other case I want to raise the Exception as it is.
I have pasted the dummy code of my approach
Please let me know if this a reasonable approach or is there a better way to do this?
class RunTimeError(Exception):
pass
class ExeHandlingDecorator:
def __init__(self,method):
self._method=method
self._clean_up=HelperClass.stop
self._forced_exit=os._exit
self._exit_code=255
def __call__(self,*args,**kwargs):
try:
return self._method(*args,**kwargs)
except RunTimeError as error:
print(error)
self._clean_up()
self._forced_exit(self._exit_code)
except Exception as error:
raise error
class TestDummy(unittest.TestCase):
#classmethod
def setUpClass(cls):
"""setup test"""
#ExeHandlingDecorator
def __helper(self):
try:
"""Testing Environment created here,
RunTimeError might be raised by the called methods in this block"""
finally:
"""Environment clean up done here"""
def test_1(self):
data_to_be_asserted=self.__helper()

Related

How to write unittest for myexception and raise? [duplicate]

How does one write a unit test that fails only if a function doesn't throw an expected exception?
Use TestCase.assertRaises (or TestCase.failUnlessRaises) from the unittest module, for example:
import mymod
class MyTestCase(unittest.TestCase):
def test1(self):
self.assertRaises(SomeCoolException, mymod.myfunc)
Since Python 2.7 you can use context manager to get ahold of the actual Exception object thrown:
import unittest
def broken_function():
raise Exception('This is broken')
class MyTestCase(unittest.TestCase):
def test(self):
with self.assertRaises(Exception) as context:
broken_function()
self.assertTrue('This is broken' in context.exception)
if __name__ == '__main__':
unittest.main()
assertRaises
In Python 3.5, you have to wrap context.exception in str, otherwise you'll get a TypeError
self.assertTrue('This is broken' in str(context.exception))
The code in my previous answer can be simplified to:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction)
And if a function takes arguments, just pass them into assertRaises like this:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction, arg1, arg2)
How do you test that a Python function throws an exception?
How does one write a test that fails only if a function doesn't throw
an expected exception?
Short Answer:
Use the self.assertRaises method as a context manager:
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
Demonstration
The best practice approach is fairly easy to demonstrate in a Python shell.
The unittest library
In Python 2.7 or 3:
import unittest
In Python 2.6, you can install a backport of 2.7's unittest library, called unittest2, and just alias that as unittest:
import unittest2 as unittest
Example tests
Now, paste into your Python shell the following test of Python's type-safety:
class MyTestCase(unittest.TestCase):
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
def test_2_cannot_add_int_and_str(self):
import operator
self.assertRaises(TypeError, operator.add, 1, '1')
Test one uses assertRaises as a context manager, which ensures that the error is properly caught and cleaned up, while recorded.
We could also write it without the context manager, see test two. The first argument would be the error type you expect to raise, the second argument, the function you are testing, and the remaining args and keyword args will be passed to that function.
I think it's far more simple, readable, and maintainable to just to use the context manager.
Running the tests
To run the tests:
unittest.main(exit=False)
In Python 2.6, you'll probably need the following:
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
And your terminal should output the following:
..
----------------------------------------------------------------------
Ran 2 tests in 0.007s
OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>
And we see that as we expect, attempting to add a 1 and a '1' result in a TypeError.
For more verbose output, try this:
unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
Your code should follow this pattern (this is a unittest module style test):
def test_afunction_throws_exception(self):
try:
afunction()
except ExpectedException:
pass
except Exception:
self.fail('unexpected exception raised')
else:
self.fail('ExpectedException not raised')
On Python < 2.7 this construct is useful for checking for specific values in the expected exception. The unittest function assertRaises only checks if an exception was raised.
From http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/:
First, here is the corresponding (still dum :p) function in file dum_function.py:
def square_value(a):
"""
Returns the square value of a.
"""
try:
out = a*a
except TypeError:
raise TypeError("Input should be a string:")
return out
Here is the test to be performed (only this test is inserted):
import dum_function as df # Import function module
import unittest
class Test(unittest.TestCase):
"""
The class inherits from unittest
"""
def setUp(self):
"""
This method is called before each test
"""
self.false_int = "A"
def tearDown(self):
"""
This method is called after each test
"""
pass
#---
## TESTS
def test_square_value(self):
# assertRaises(excClass, callableObj) prototype
self.assertRaises(TypeError, df.square_value(self.false_int))
if __name__ == "__main__":
unittest.main()
We are now ready to test our function! Here is what happens when trying to run the test:
======================================================================
ERROR: test_square_value (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_dum_function.py", line 22, in test_square_value
self.assertRaises(TypeError, df.square_value(self.false_int))
File "/home/jlengrand/Desktop/function.py", line 8, in square_value
raise TypeError("Input should be a string:")
TypeError: Input should be a string:
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
The TypeError is actually raised, and generates a test failure. The problem is that this is exactly the behavior we wanted :s.
To avoid this error, simply run the function using lambda in the test call:
self.assertRaises(TypeError, lambda: df.square_value(self.false_int))
The final output:
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Perfect!
... and for me is perfect too!!
Thanks a lot, Mr. Julien Lengrand-Lambert.
This test assert actually returns a false positive. That happens because the lambda inside the 'assertRaises' is the unit that raises type error and not the tested function.
As I haven't seen any detailed explanation on how to check if we got a specific exception among a list of accepted one using context manager, or other exception details I will add mine (checked on Python 3.8).
If I just want to check that function is raising for instance TypeError, I would write:
with self.assertRaises(TypeError):
function_raising_some_exception(parameters)
If I want to check that function is raising either TypeError or IndexError, I would write:
with self.assertRaises((TypeError,IndexError)):
function_raising_some_exception(parameters)
And if I want even more details about the Exception raised I could catch it in a context like this:
# Here I catch any exception
with self.assertRaises(Exception) as e:
function_raising_some_exception(parameters)
# Here I check actual exception type (but I could
# check anything else about that specific exception,
# like it's actual message or values stored in the exception)
self.assertTrue(type(e.exception) in [TypeError,MatrixIsSingular])
If you are using pytest you can use pytest.raises(Exception):
Example:
def test_div_zero():
with pytest.raises(ZeroDivisionError):
1/0
And the result:
$ py.test
================= test session starts =================
platform linux2 -- Python 2.6.6 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collected 1 items
tests/test_div_zero.py:6: test_div_zero PASSED
Or you can build your own contextmanager to check if the exception was raised.
import contextlib
#contextlib.contextmanager
def raises(exception):
try:
yield
except exception as e:
assert True
else:
assert False
And then you can use raises like this:
with raises(Exception):
print "Hola" # Calls assert False
with raises(Exception):
raise Exception # Calls assert True
If you are using Python 3, in order to assert an exception along with its message, you can use assertRaises in context manager and pass the message as a msg keyword argument like so:
import unittest
def your_function():
raise RuntimeError('your exception message')
class YourTestCase(unittest.TestCase):
def test(self):
with self.assertRaises(RuntimeError, msg='your exception message'):
your_function()
if __name__ == '__main__':
unittest.main()
I use doctest[1] almost everywhere because I like the fact that I document and test my functions at the same time.
Have a look at this code:
def throw_up(something, gowrong=False):
"""
>>> throw_up('Fish n Chips')
Traceback (most recent call last):
...
Exception: Fish n Chips
>>> throw_up('Fish n Chips', gowrong=True)
'I feel fine!'
"""
if gowrong:
return "I feel fine!"
raise Exception(something)
if __name__ == '__main__':
import doctest
doctest.testmod()
If you put this example in a module and run it from the command line both test cases are evaluated and checked.
[1] Python documentation: 23.2 doctest -- Test interactive Python examples
There are a lot of answers here. The code shows how we can create an Exception, how we can use that exception in our methods, and finally, how you can verify in a unit test, the correct exceptions being raised.
import unittest
class DeviceException(Exception):
def __init__(self, msg, code):
self.msg = msg
self.code = code
def __str__(self):
return repr("Error {}: {}".format(self.code, self.msg))
class MyDevice(object):
def __init__(self):
self.name = 'DefaultName'
def setParameter(self, param, value):
if isinstance(value, str):
setattr(self, param , value)
else:
raise DeviceException('Incorrect type of argument passed. Name expects a string', 100001)
def getParameter(self, param):
return getattr(self, param)
class TestMyDevice(unittest.TestCase):
def setUp(self):
self.dev1 = MyDevice()
def tearDown(self):
del self.dev1
def test_name(self):
""" Test for valid input for name parameter """
self.dev1.setParameter('name', 'MyDevice')
name = self.dev1.getParameter('name')
self.assertEqual(name, 'MyDevice')
def test_invalid_name(self):
""" Test to check if error is raised if invalid type of input is provided """
self.assertRaises(DeviceException, self.dev1.setParameter, 'name', 1234)
def test_exception_message(self):
""" Test to check if correct exception message and code is raised when incorrect value is passed """
with self.assertRaises(DeviceException) as cm:
self.dev1.setParameter('name', 1234)
self.assertEqual(cm.exception.msg, 'Incorrect type of argument passed. Name expects a string', 'mismatch in expected error message')
self.assertEqual(cm.exception.code, 100001, 'mismatch in expected error code')
if __name__ == '__main__':
unittest.main()
I just discovered that the Mock library provides an assertRaisesWithMessage() method (in its unittest.TestCase subclass), which will check not only that the expected exception is raised, but also that it is raised with the expected message:
from testcase import TestCase
import mymod
class MyTestCase(TestCase):
def test1(self):
self.assertRaisesWithMessage(SomeCoolException,
'expected message',
mymod.myfunc)
There are 4 options (you'll find full example in the end):
assertRaises with context manager
def test_raises(self):
with self.assertRaises(RuntimeError):
raise RuntimeError()
If you want to check the exception message (see the "assertRaisesRegex with context manager" option below to check only part of it):
def test_raises(self):
with self.assertRaises(RuntimeError) as error:
raise RuntimeError("your exception message")
self.assertEqual(str(error.exception), "your exception message")
assertRaises one-liner
Pay attention: instead of function call, here you use your function as callable (without round brackets).
def test_raises(self):
self.assertRaises(RuntimeError, your_function)
assertRaisesRegex with context manager
Second parameter is regex expression and is mandatory. Handy when you want check only part of the exception message.
def test_raises_regex(self):
with self.assertRaisesRegex(RuntimeError, r'.* exception message'):
raise RuntimeError('your exception message')
assertRaisesRegex one-liner
Second parameter is regex expression and is mandatory. Handy when you want check only part of the exception message.
Pay attention: instead of function call, here you use your function as callable (without round brackets).
def test_raises_regex(self):
self.assertRaisesRegex(RuntimeError, r'.* exception message', your_function)
Full code example:
import unittest
def your_function():
raise RuntimeError('your exception message')
class YourTestCase(unittest.TestCase):
def test_1_raises_context_manager(self):
with self.assertRaises(RuntimeError):
your_function()
def test_1b_raises_context_manager_and_error_message(self):
with self.assertRaises(RuntimeError) as error:
your_function()
self.assertEqual(str(error.exception), "your exception message")
def test_2_raises_oneliner(self):
self.assertRaises(RuntimeError, your_function)
def test_3_raises_regex_context_manager(self):
with self.assertRaisesRegex(RuntimeError, r'.* exception message'):
your_function()
def test_4_raises_regex_oneliner(self):
self.assertRaisesRegex(RuntimeError, r'.* exception message', your_function)
if __name__ == '__main__':
unittest.main()
Although it's up to developer which style to follow I prefer both methods using context manager.
You can use assertRaises from the unittest module:
import unittest
class TestClass():
def raises_exception(self):
raise Exception("test")
class MyTestCase(unittest.TestCase):
def test_if_method_raises_correct_exception(self):
test_class = TestClass()
# Note that you don’t use () when passing the method to assertRaises
self.assertRaises(Exception, test_class.raises_exception)
For those on Django, you can use context manager to run the faulty function and assert it raises the exception with a certain message using assertRaisesMessage
with self.assertRaisesMessage(SomeException,'Some error message e.g 404 Not Found'):
faulty_funtion()
For await/async aiounittest there is a slightly different pattern:
https://aiounittest.readthedocs.io/en/latest/asynctestcase.html#aiounittest.AsyncTestCase
async def test_await_async_fail(self):
with self.assertRaises(Exception) as e:
await async_one()
This will raise TypeError if setting stock_id to an Integer in this class will throw the error, the test will pass if this happens and fails otherwise
def set_string(prop, value):
if not isinstance(value, str):
raise TypeError("i told you i take strings only ")
return value
class BuyVolume(ndb.Model):
stock_id = ndb.StringProperty(validator=set_string)
from pytest import raises
buy_volume_instance: BuyVolume = BuyVolume()
with raises(TypeError):
buy_volume_instance.stock_id = 25
Unit testing with unittest would be preferred, but if you would like a quick fix, we can catch the exception, assign it to a variable, and see if that variable is an instance of that exception class.
Lets assume our bad function throws a ValueError.
try:
bad_function()
except ValueError as e:
assert isinstance(e, ValueError)
While all the answers are perfectly fine, I was looking for a way to test if a function raised an exception without relying on unit testing frameworks and having to write test classes.
I ended up writing the following:
def assert_error(e, x):
try:
e(x)
except:
return
raise AssertionError()
def failing_function(x):
raise ValueError()
def dummy_function(x):
return x
if __name__=="__main__":
assert_error(failing_function, 0)
assert_error(dummy_function, 0)
And it fails on the right line:
Traceback (most recent call last):
File "assert_error.py", line 16, in <module>
assert_error(dummy_function, 0)
File "assert_error.py", line 6, in assert_error
raise AssertionError()
AssertionError

testing class init method with unittest [duplicate]

How does one write a unit test that fails only if a function doesn't throw an expected exception?
Use TestCase.assertRaises (or TestCase.failUnlessRaises) from the unittest module, for example:
import mymod
class MyTestCase(unittest.TestCase):
def test1(self):
self.assertRaises(SomeCoolException, mymod.myfunc)
Since Python 2.7 you can use context manager to get ahold of the actual Exception object thrown:
import unittest
def broken_function():
raise Exception('This is broken')
class MyTestCase(unittest.TestCase):
def test(self):
with self.assertRaises(Exception) as context:
broken_function()
self.assertTrue('This is broken' in context.exception)
if __name__ == '__main__':
unittest.main()
assertRaises
In Python 3.5, you have to wrap context.exception in str, otherwise you'll get a TypeError
self.assertTrue('This is broken' in str(context.exception))
The code in my previous answer can be simplified to:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction)
And if a function takes arguments, just pass them into assertRaises like this:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction, arg1, arg2)
How do you test that a Python function throws an exception?
How does one write a test that fails only if a function doesn't throw
an expected exception?
Short Answer:
Use the self.assertRaises method as a context manager:
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
Demonstration
The best practice approach is fairly easy to demonstrate in a Python shell.
The unittest library
In Python 2.7 or 3:
import unittest
In Python 2.6, you can install a backport of 2.7's unittest library, called unittest2, and just alias that as unittest:
import unittest2 as unittest
Example tests
Now, paste into your Python shell the following test of Python's type-safety:
class MyTestCase(unittest.TestCase):
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
def test_2_cannot_add_int_and_str(self):
import operator
self.assertRaises(TypeError, operator.add, 1, '1')
Test one uses assertRaises as a context manager, which ensures that the error is properly caught and cleaned up, while recorded.
We could also write it without the context manager, see test two. The first argument would be the error type you expect to raise, the second argument, the function you are testing, and the remaining args and keyword args will be passed to that function.
I think it's far more simple, readable, and maintainable to just to use the context manager.
Running the tests
To run the tests:
unittest.main(exit=False)
In Python 2.6, you'll probably need the following:
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
And your terminal should output the following:
..
----------------------------------------------------------------------
Ran 2 tests in 0.007s
OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>
And we see that as we expect, attempting to add a 1 and a '1' result in a TypeError.
For more verbose output, try this:
unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
Your code should follow this pattern (this is a unittest module style test):
def test_afunction_throws_exception(self):
try:
afunction()
except ExpectedException:
pass
except Exception:
self.fail('unexpected exception raised')
else:
self.fail('ExpectedException not raised')
On Python < 2.7 this construct is useful for checking for specific values in the expected exception. The unittest function assertRaises only checks if an exception was raised.
From http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/:
First, here is the corresponding (still dum :p) function in file dum_function.py:
def square_value(a):
"""
Returns the square value of a.
"""
try:
out = a*a
except TypeError:
raise TypeError("Input should be a string:")
return out
Here is the test to be performed (only this test is inserted):
import dum_function as df # Import function module
import unittest
class Test(unittest.TestCase):
"""
The class inherits from unittest
"""
def setUp(self):
"""
This method is called before each test
"""
self.false_int = "A"
def tearDown(self):
"""
This method is called after each test
"""
pass
#---
## TESTS
def test_square_value(self):
# assertRaises(excClass, callableObj) prototype
self.assertRaises(TypeError, df.square_value(self.false_int))
if __name__ == "__main__":
unittest.main()
We are now ready to test our function! Here is what happens when trying to run the test:
======================================================================
ERROR: test_square_value (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_dum_function.py", line 22, in test_square_value
self.assertRaises(TypeError, df.square_value(self.false_int))
File "/home/jlengrand/Desktop/function.py", line 8, in square_value
raise TypeError("Input should be a string:")
TypeError: Input should be a string:
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
The TypeError is actually raised, and generates a test failure. The problem is that this is exactly the behavior we wanted :s.
To avoid this error, simply run the function using lambda in the test call:
self.assertRaises(TypeError, lambda: df.square_value(self.false_int))
The final output:
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Perfect!
... and for me is perfect too!!
Thanks a lot, Mr. Julien Lengrand-Lambert.
This test assert actually returns a false positive. That happens because the lambda inside the 'assertRaises' is the unit that raises type error and not the tested function.
As I haven't seen any detailed explanation on how to check if we got a specific exception among a list of accepted one using context manager, or other exception details I will add mine (checked on Python 3.8).
If I just want to check that function is raising for instance TypeError, I would write:
with self.assertRaises(TypeError):
function_raising_some_exception(parameters)
If I want to check that function is raising either TypeError or IndexError, I would write:
with self.assertRaises((TypeError,IndexError)):
function_raising_some_exception(parameters)
And if I want even more details about the Exception raised I could catch it in a context like this:
# Here I catch any exception
with self.assertRaises(Exception) as e:
function_raising_some_exception(parameters)
# Here I check actual exception type (but I could
# check anything else about that specific exception,
# like it's actual message or values stored in the exception)
self.assertTrue(type(e.exception) in [TypeError,MatrixIsSingular])
If you are using pytest you can use pytest.raises(Exception):
Example:
def test_div_zero():
with pytest.raises(ZeroDivisionError):
1/0
And the result:
$ py.test
================= test session starts =================
platform linux2 -- Python 2.6.6 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collected 1 items
tests/test_div_zero.py:6: test_div_zero PASSED
Or you can build your own contextmanager to check if the exception was raised.
import contextlib
#contextlib.contextmanager
def raises(exception):
try:
yield
except exception as e:
assert True
else:
assert False
And then you can use raises like this:
with raises(Exception):
print "Hola" # Calls assert False
with raises(Exception):
raise Exception # Calls assert True
If you are using Python 3, in order to assert an exception along with its message, you can use assertRaises in context manager and pass the message as a msg keyword argument like so:
import unittest
def your_function():
raise RuntimeError('your exception message')
class YourTestCase(unittest.TestCase):
def test(self):
with self.assertRaises(RuntimeError, msg='your exception message'):
your_function()
if __name__ == '__main__':
unittest.main()
I use doctest[1] almost everywhere because I like the fact that I document and test my functions at the same time.
Have a look at this code:
def throw_up(something, gowrong=False):
"""
>>> throw_up('Fish n Chips')
Traceback (most recent call last):
...
Exception: Fish n Chips
>>> throw_up('Fish n Chips', gowrong=True)
'I feel fine!'
"""
if gowrong:
return "I feel fine!"
raise Exception(something)
if __name__ == '__main__':
import doctest
doctest.testmod()
If you put this example in a module and run it from the command line both test cases are evaluated and checked.
[1] Python documentation: 23.2 doctest -- Test interactive Python examples
There are a lot of answers here. The code shows how we can create an Exception, how we can use that exception in our methods, and finally, how you can verify in a unit test, the correct exceptions being raised.
import unittest
class DeviceException(Exception):
def __init__(self, msg, code):
self.msg = msg
self.code = code
def __str__(self):
return repr("Error {}: {}".format(self.code, self.msg))
class MyDevice(object):
def __init__(self):
self.name = 'DefaultName'
def setParameter(self, param, value):
if isinstance(value, str):
setattr(self, param , value)
else:
raise DeviceException('Incorrect type of argument passed. Name expects a string', 100001)
def getParameter(self, param):
return getattr(self, param)
class TestMyDevice(unittest.TestCase):
def setUp(self):
self.dev1 = MyDevice()
def tearDown(self):
del self.dev1
def test_name(self):
""" Test for valid input for name parameter """
self.dev1.setParameter('name', 'MyDevice')
name = self.dev1.getParameter('name')
self.assertEqual(name, 'MyDevice')
def test_invalid_name(self):
""" Test to check if error is raised if invalid type of input is provided """
self.assertRaises(DeviceException, self.dev1.setParameter, 'name', 1234)
def test_exception_message(self):
""" Test to check if correct exception message and code is raised when incorrect value is passed """
with self.assertRaises(DeviceException) as cm:
self.dev1.setParameter('name', 1234)
self.assertEqual(cm.exception.msg, 'Incorrect type of argument passed. Name expects a string', 'mismatch in expected error message')
self.assertEqual(cm.exception.code, 100001, 'mismatch in expected error code')
if __name__ == '__main__':
unittest.main()
I just discovered that the Mock library provides an assertRaisesWithMessage() method (in its unittest.TestCase subclass), which will check not only that the expected exception is raised, but also that it is raised with the expected message:
from testcase import TestCase
import mymod
class MyTestCase(TestCase):
def test1(self):
self.assertRaisesWithMessage(SomeCoolException,
'expected message',
mymod.myfunc)
There are 4 options (you'll find full example in the end):
assertRaises with context manager
def test_raises(self):
with self.assertRaises(RuntimeError):
raise RuntimeError()
If you want to check the exception message (see the "assertRaisesRegex with context manager" option below to check only part of it):
def test_raises(self):
with self.assertRaises(RuntimeError) as error:
raise RuntimeError("your exception message")
self.assertEqual(str(error.exception), "your exception message")
assertRaises one-liner
Pay attention: instead of function call, here you use your function as callable (without round brackets).
def test_raises(self):
self.assertRaises(RuntimeError, your_function)
assertRaisesRegex with context manager
Second parameter is regex expression and is mandatory. Handy when you want check only part of the exception message.
def test_raises_regex(self):
with self.assertRaisesRegex(RuntimeError, r'.* exception message'):
raise RuntimeError('your exception message')
assertRaisesRegex one-liner
Second parameter is regex expression and is mandatory. Handy when you want check only part of the exception message.
Pay attention: instead of function call, here you use your function as callable (without round brackets).
def test_raises_regex(self):
self.assertRaisesRegex(RuntimeError, r'.* exception message', your_function)
Full code example:
import unittest
def your_function():
raise RuntimeError('your exception message')
class YourTestCase(unittest.TestCase):
def test_1_raises_context_manager(self):
with self.assertRaises(RuntimeError):
your_function()
def test_1b_raises_context_manager_and_error_message(self):
with self.assertRaises(RuntimeError) as error:
your_function()
self.assertEqual(str(error.exception), "your exception message")
def test_2_raises_oneliner(self):
self.assertRaises(RuntimeError, your_function)
def test_3_raises_regex_context_manager(self):
with self.assertRaisesRegex(RuntimeError, r'.* exception message'):
your_function()
def test_4_raises_regex_oneliner(self):
self.assertRaisesRegex(RuntimeError, r'.* exception message', your_function)
if __name__ == '__main__':
unittest.main()
Although it's up to developer which style to follow I prefer both methods using context manager.
You can use assertRaises from the unittest module:
import unittest
class TestClass():
def raises_exception(self):
raise Exception("test")
class MyTestCase(unittest.TestCase):
def test_if_method_raises_correct_exception(self):
test_class = TestClass()
# Note that you don’t use () when passing the method to assertRaises
self.assertRaises(Exception, test_class.raises_exception)
For those on Django, you can use context manager to run the faulty function and assert it raises the exception with a certain message using assertRaisesMessage
with self.assertRaisesMessage(SomeException,'Some error message e.g 404 Not Found'):
faulty_funtion()
For await/async aiounittest there is a slightly different pattern:
https://aiounittest.readthedocs.io/en/latest/asynctestcase.html#aiounittest.AsyncTestCase
async def test_await_async_fail(self):
with self.assertRaises(Exception) as e:
await async_one()
This will raise TypeError if setting stock_id to an Integer in this class will throw the error, the test will pass if this happens and fails otherwise
def set_string(prop, value):
if not isinstance(value, str):
raise TypeError("i told you i take strings only ")
return value
class BuyVolume(ndb.Model):
stock_id = ndb.StringProperty(validator=set_string)
from pytest import raises
buy_volume_instance: BuyVolume = BuyVolume()
with raises(TypeError):
buy_volume_instance.stock_id = 25
Unit testing with unittest would be preferred, but if you would like a quick fix, we can catch the exception, assign it to a variable, and see if that variable is an instance of that exception class.
Lets assume our bad function throws a ValueError.
try:
bad_function()
except ValueError as e:
assert isinstance(e, ValueError)
While all the answers are perfectly fine, I was looking for a way to test if a function raised an exception without relying on unit testing frameworks and having to write test classes.
I ended up writing the following:
def assert_error(e, x):
try:
e(x)
except:
return
raise AssertionError()
def failing_function(x):
raise ValueError()
def dummy_function(x):
return x
if __name__=="__main__":
assert_error(failing_function, 0)
assert_error(dummy_function, 0)
And it fails on the right line:
Traceback (most recent call last):
File "assert_error.py", line 16, in <module>
assert_error(dummy_function, 0)
File "assert_error.py", line 6, in assert_error
raise AssertionError()
AssertionError

Abstract base classes and Exceptions

Question
Why do virtual subclasses of an abstract Exception created using the ABCMeta.register not match under the except clause?
Background
I'd like to ensure that exceptions that get thrown by a package that I'm using are converted to MyException, so that code which imports my module can catch any exception my module throws using except MyException: instead of except Exception so that they don't have to depend on an implementation detail (the fact that I'm using a third-party package).
Example
To do this, I've tried registering an OtherException as MyException using an abstract base class:
# Tested with python-3.6
from abc import ABC
class MyException(Exception, ABC):
pass
class OtherException(Exception):
"""Other exception I can't change"""
pass
MyException.register(OtherException)
assert issubclass(OtherException, MyException) # passes
try:
raise OtherException("Some OtherException")
except MyException:
print("Caught MyException")
except Exception as e:
print("Caught Exception: {}".format(e))
The assertion passes (as expected), but the exception falls to the second block:
Caught Exception: Some OtherException
Alright, I looked into this some more. The answer is that it's a long-outstanding open issue in Python3 (there since the very first release) and apparently was first reported in 2011. As Guido said in the comments, "I agree it's a bug and should be fixed." Unfortunately, this bug has lingered due to concerns about the performance of the fix and some corner cases that need to be handled.
The core issue is that the exception matching routine PyErr_GivenExceptionMatches in errors.c uses PyType_IsSubtype and not PyObject_IsSubclass. Since types and objects are supposed to be the same in python3, this amounts to a bug.
I've made a PR to python3 that seems to cover all of the issues discussed in the thread, but given the history I'm not super optimistic it's going to get merged soon. We'll see.
The why is easy:
from abc import ABC
class MyException(Exception, ABC):
pass
class OtherException(Exception):
"""Other exception I can't change"""
pass
MyException.register(OtherException)
assert issubclass(OtherException, MyException) # passes
assert OtherException in MyException.__subclasses__() # fails
Edit: This assert mimics the outcome of the except clause, but does not represent what actually happens. Look at the accept answer for an explanation.
The workaround also is easy:
class OtherException(Exception):
pass
class AnotherException(Exception):
pass
MyException = (OtherException, AnotherException)
It seems that CPython once again takes some shortcuts and doesn't bother calling the metaclass's __instancecheck__ method for the classes listed in except clauses.
We can test this by implementing a custom metaclass with __instancecheck__ and __subclasscheck__ methods:
class OtherException(Exception):
pass
class Meta(type):
def __instancecheck__(self, value):
print('instancecheck called')
return True
def __subclasscheck__(self, value):
print('subclasscheck called')
return True
class MyException(Exception, metaclass=Meta):
pass
try:
raise OtherException("Some OtherException")
except MyException:
print("Caught MyException")
except Exception as e:
print("Caught Exception: {}".format(e))
# output:
# Caught Exception: Some OtherException
We can see that the print statements in the metaclass aren't executed.
I don't know if this is intended/documented behavior or not. The closest thing to relevant information I could find was from the exception handling tutorial:
A class in an except clause is compatible with an exception if it is
the same class or a base class thereof
Does that mean that classes have to be real subclasses (i.e. the parent class must be part of the subclass's MRO)? I don't know.
As for a workaround: You can simply make MyException an alias of OtherException.
class OtherException(Exception):
pass
MyException = OtherException
try:
raise OtherException("Some OtherException")
except MyException:
print("Caught MyException")
except Exception as e:
print("Caught Exception: {}".format(e))
# output:
# Caught MyException
In the case that you have to catch multiple different exceptions that don't have a common base class, you can define MyException as a tuple:
MyException = (OtherException, AnotherException)
Well, this doesn't really answer your question directly, but if you're trying to ensure a block of code calls your exception, you could take a different strategy by intercepting with a context manager.
In [78]: class WithException:
...:
...: def __enter__(self):
...: pass
...: def __exit__(self, exc, msg, traceback):
...: if exc is OtherException:
...: raise MyException(msg)
...:
In [79]: with WithException():
...: raise OtherException('aaaaaaarrrrrrggggh')
...:
---------------------------------------------------------------------------
OtherException Traceback (most recent call last)
<ipython-input-79-a0a23168647e> in <module>()
1 with WithException():
----> 2 raise OtherException('aaaaaaarrrrrrggggh')
OtherException: aaaaaaarrrrrrggggh
During handling of the above exception, another exception occurred:
MyException Traceback (most recent call last)
<ipython-input-79-a0a23168647e> in <module>()
1 with WithException():
----> 2 raise OtherException('aaaaaaarrrrrrggggh')
<ipython-input-78-dba8b409a6fd> in __exit__(self, exc, msg, traceback)
5 def __exit__(self, exc, msg, traceback):
6 if exc is OtherException:
----> 7 raise MyException(msg)
8
MyException: aaaaaaarrrrrrggggh

python unittest assertRaises throws exception when assertRaises fails

I've got code where assertRaises throws an exception when assertRaises fails. I thought that if assertRaises fails then the test would fail and I'd get a report at the end that says the test failed. I wasn't expecting the exception to be thrown. Below is my code. I'm I doing something wrong? I'm using Python 2.6.2.
import unittest
class myClass:
def getName(self):
raise myExcOne, "my exception one"
#raise myExcTwo, "my exception two"
#return "a"
class myExcOne(Exception):
"exception one"
class myExcTwo(Exception):
"exception two"
class test_myClass(unittest.TestCase):
def setUp(self):
self.myClass = myClass()
def testgetNameEmpty(self):
#self.assertRaises(myExcOne,self.myClass.getName)
#self.assertRaises(myExcTwo,self.myClass.getName)
try:
self.assertRaises(myExcTwo,self.myClass.getName)
except Exception as e:
pass
if __name__ == "__main__":
#unittest.main()
suite = unittest.TestLoader().loadTestsFromTestCase(test_myClass)
unittest.TextTestRunner(verbosity=2).run(suite)
The code as posted is wrong. For a start, class myClass(): shoudl be class myClass:. Also if name == "main": should be:
if __name__ == "__main__":
unittest.main()
Apart from these problems, this fails because getName() is raising exception myExcOne and your test expects exception myExcTwo.
Here is some code that works. Please edit the code in your question so that it is easy for us to cut and paste it into a Python session:
import unittest
class myExcOne(Exception): "exception one"
class myExcTwo(Exception): "exception two"
class myClass:
def getName(self):
raise myExcTwo
class test_myClass(unittest.TestCase):
def setUp(self):
self.myClass = myClass()
def testgetNameEmpty(self):
#self.assertRaises(myExcOne,self.myClass.getName)
self.assertRaises(myExcTwo,self.myClass.getName)
if __name__ == "__main__":
unittest.main()
Starting with an aside, the () after the classname in a class statement is perfectly correct in modern Python -- not an error at all.
On the meat of the issue, assertRaises(MyException, foo) is documented to propagate exceptions raised by calling foo() whose type is NOT a subclass of MyException -- it only catches MyException and subclasses thereof. As your code raises an exception of one type and your test expects one of a different unrelated type, the raised exception will then propagate, as per the docs of the unittest module, here, and I quote:
The test passes if exception is raised, is an error if another exception is raised, or fails if no exception is raised.
And "is an error" means "propagates the other exception".
As you catch the exception propagating in your try/except block, you nullify the error, and there's nothing left for unittest to diagnose. If your purpose is to turn this error into a failure (a debatable strategy...), your except block should call self.fail.

How do you test that a Python function throws an exception?

How does one write a unit test that fails only if a function doesn't throw an expected exception?
Use TestCase.assertRaises (or TestCase.failUnlessRaises) from the unittest module, for example:
import mymod
class MyTestCase(unittest.TestCase):
def test1(self):
self.assertRaises(SomeCoolException, mymod.myfunc)
Since Python 2.7 you can use context manager to get ahold of the actual Exception object thrown:
import unittest
def broken_function():
raise Exception('This is broken')
class MyTestCase(unittest.TestCase):
def test(self):
with self.assertRaises(Exception) as context:
broken_function()
self.assertTrue('This is broken' in context.exception)
if __name__ == '__main__':
unittest.main()
assertRaises
In Python 3.5, you have to wrap context.exception in str, otherwise you'll get a TypeError
self.assertTrue('This is broken' in str(context.exception))
The code in my previous answer can be simplified to:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction)
And if a function takes arguments, just pass them into assertRaises like this:
def test_afunction_throws_exception(self):
self.assertRaises(ExpectedException, afunction, arg1, arg2)
How do you test that a Python function throws an exception?
How does one write a test that fails only if a function doesn't throw
an expected exception?
Short Answer:
Use the self.assertRaises method as a context manager:
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
Demonstration
The best practice approach is fairly easy to demonstrate in a Python shell.
The unittest library
In Python 2.7 or 3:
import unittest
In Python 2.6, you can install a backport of 2.7's unittest library, called unittest2, and just alias that as unittest:
import unittest2 as unittest
Example tests
Now, paste into your Python shell the following test of Python's type-safety:
class MyTestCase(unittest.TestCase):
def test_1_cannot_add_int_and_str(self):
with self.assertRaises(TypeError):
1 + '1'
def test_2_cannot_add_int_and_str(self):
import operator
self.assertRaises(TypeError, operator.add, 1, '1')
Test one uses assertRaises as a context manager, which ensures that the error is properly caught and cleaned up, while recorded.
We could also write it without the context manager, see test two. The first argument would be the error type you expect to raise, the second argument, the function you are testing, and the remaining args and keyword args will be passed to that function.
I think it's far more simple, readable, and maintainable to just to use the context manager.
Running the tests
To run the tests:
unittest.main(exit=False)
In Python 2.6, you'll probably need the following:
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
And your terminal should output the following:
..
----------------------------------------------------------------------
Ran 2 tests in 0.007s
OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>
And we see that as we expect, attempting to add a 1 and a '1' result in a TypeError.
For more verbose output, try this:
unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))
Your code should follow this pattern (this is a unittest module style test):
def test_afunction_throws_exception(self):
try:
afunction()
except ExpectedException:
pass
except Exception:
self.fail('unexpected exception raised')
else:
self.fail('ExpectedException not raised')
On Python < 2.7 this construct is useful for checking for specific values in the expected exception. The unittest function assertRaises only checks if an exception was raised.
From http://www.lengrand.fr/2011/12/pythonunittest-assertraises-raises-error/:
First, here is the corresponding (still dum :p) function in file dum_function.py:
def square_value(a):
"""
Returns the square value of a.
"""
try:
out = a*a
except TypeError:
raise TypeError("Input should be a string:")
return out
Here is the test to be performed (only this test is inserted):
import dum_function as df # Import function module
import unittest
class Test(unittest.TestCase):
"""
The class inherits from unittest
"""
def setUp(self):
"""
This method is called before each test
"""
self.false_int = "A"
def tearDown(self):
"""
This method is called after each test
"""
pass
#---
## TESTS
def test_square_value(self):
# assertRaises(excClass, callableObj) prototype
self.assertRaises(TypeError, df.square_value(self.false_int))
if __name__ == "__main__":
unittest.main()
We are now ready to test our function! Here is what happens when trying to run the test:
======================================================================
ERROR: test_square_value (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test_dum_function.py", line 22, in test_square_value
self.assertRaises(TypeError, df.square_value(self.false_int))
File "/home/jlengrand/Desktop/function.py", line 8, in square_value
raise TypeError("Input should be a string:")
TypeError: Input should be a string:
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
The TypeError is actually raised, and generates a test failure. The problem is that this is exactly the behavior we wanted :s.
To avoid this error, simply run the function using lambda in the test call:
self.assertRaises(TypeError, lambda: df.square_value(self.false_int))
The final output:
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Perfect!
... and for me is perfect too!!
Thanks a lot, Mr. Julien Lengrand-Lambert.
This test assert actually returns a false positive. That happens because the lambda inside the 'assertRaises' is the unit that raises type error and not the tested function.
As I haven't seen any detailed explanation on how to check if we got a specific exception among a list of accepted one using context manager, or other exception details I will add mine (checked on Python 3.8).
If I just want to check that function is raising for instance TypeError, I would write:
with self.assertRaises(TypeError):
function_raising_some_exception(parameters)
If I want to check that function is raising either TypeError or IndexError, I would write:
with self.assertRaises((TypeError,IndexError)):
function_raising_some_exception(parameters)
And if I want even more details about the Exception raised I could catch it in a context like this:
# Here I catch any exception
with self.assertRaises(Exception) as e:
function_raising_some_exception(parameters)
# Here I check actual exception type (but I could
# check anything else about that specific exception,
# like it's actual message or values stored in the exception)
self.assertTrue(type(e.exception) in [TypeError,MatrixIsSingular])
If you are using pytest you can use pytest.raises(Exception):
Example:
def test_div_zero():
with pytest.raises(ZeroDivisionError):
1/0
And the result:
$ py.test
================= test session starts =================
platform linux2 -- Python 2.6.6 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collected 1 items
tests/test_div_zero.py:6: test_div_zero PASSED
Or you can build your own contextmanager to check if the exception was raised.
import contextlib
#contextlib.contextmanager
def raises(exception):
try:
yield
except exception as e:
assert True
else:
assert False
And then you can use raises like this:
with raises(Exception):
print "Hola" # Calls assert False
with raises(Exception):
raise Exception # Calls assert True
If you are using Python 3, in order to assert an exception along with its message, you can use assertRaises in context manager and pass the message as a msg keyword argument like so:
import unittest
def your_function():
raise RuntimeError('your exception message')
class YourTestCase(unittest.TestCase):
def test(self):
with self.assertRaises(RuntimeError, msg='your exception message'):
your_function()
if __name__ == '__main__':
unittest.main()
I use doctest[1] almost everywhere because I like the fact that I document and test my functions at the same time.
Have a look at this code:
def throw_up(something, gowrong=False):
"""
>>> throw_up('Fish n Chips')
Traceback (most recent call last):
...
Exception: Fish n Chips
>>> throw_up('Fish n Chips', gowrong=True)
'I feel fine!'
"""
if gowrong:
return "I feel fine!"
raise Exception(something)
if __name__ == '__main__':
import doctest
doctest.testmod()
If you put this example in a module and run it from the command line both test cases are evaluated and checked.
[1] Python documentation: 23.2 doctest -- Test interactive Python examples
There are a lot of answers here. The code shows how we can create an Exception, how we can use that exception in our methods, and finally, how you can verify in a unit test, the correct exceptions being raised.
import unittest
class DeviceException(Exception):
def __init__(self, msg, code):
self.msg = msg
self.code = code
def __str__(self):
return repr("Error {}: {}".format(self.code, self.msg))
class MyDevice(object):
def __init__(self):
self.name = 'DefaultName'
def setParameter(self, param, value):
if isinstance(value, str):
setattr(self, param , value)
else:
raise DeviceException('Incorrect type of argument passed. Name expects a string', 100001)
def getParameter(self, param):
return getattr(self, param)
class TestMyDevice(unittest.TestCase):
def setUp(self):
self.dev1 = MyDevice()
def tearDown(self):
del self.dev1
def test_name(self):
""" Test for valid input for name parameter """
self.dev1.setParameter('name', 'MyDevice')
name = self.dev1.getParameter('name')
self.assertEqual(name, 'MyDevice')
def test_invalid_name(self):
""" Test to check if error is raised if invalid type of input is provided """
self.assertRaises(DeviceException, self.dev1.setParameter, 'name', 1234)
def test_exception_message(self):
""" Test to check if correct exception message and code is raised when incorrect value is passed """
with self.assertRaises(DeviceException) as cm:
self.dev1.setParameter('name', 1234)
self.assertEqual(cm.exception.msg, 'Incorrect type of argument passed. Name expects a string', 'mismatch in expected error message')
self.assertEqual(cm.exception.code, 100001, 'mismatch in expected error code')
if __name__ == '__main__':
unittest.main()
I just discovered that the Mock library provides an assertRaisesWithMessage() method (in its unittest.TestCase subclass), which will check not only that the expected exception is raised, but also that it is raised with the expected message:
from testcase import TestCase
import mymod
class MyTestCase(TestCase):
def test1(self):
self.assertRaisesWithMessage(SomeCoolException,
'expected message',
mymod.myfunc)
There are 4 options (you'll find full example in the end):
assertRaises with context manager
def test_raises(self):
with self.assertRaises(RuntimeError):
raise RuntimeError()
If you want to check the exception message (see the "assertRaisesRegex with context manager" option below to check only part of it):
def test_raises(self):
with self.assertRaises(RuntimeError) as error:
raise RuntimeError("your exception message")
self.assertEqual(str(error.exception), "your exception message")
assertRaises one-liner
Pay attention: instead of function call, here you use your function as callable (without round brackets).
def test_raises(self):
self.assertRaises(RuntimeError, your_function)
assertRaisesRegex with context manager
Second parameter is regex expression and is mandatory. Handy when you want check only part of the exception message.
def test_raises_regex(self):
with self.assertRaisesRegex(RuntimeError, r'.* exception message'):
raise RuntimeError('your exception message')
assertRaisesRegex one-liner
Second parameter is regex expression and is mandatory. Handy when you want check only part of the exception message.
Pay attention: instead of function call, here you use your function as callable (without round brackets).
def test_raises_regex(self):
self.assertRaisesRegex(RuntimeError, r'.* exception message', your_function)
Full code example:
import unittest
def your_function():
raise RuntimeError('your exception message')
class YourTestCase(unittest.TestCase):
def test_1_raises_context_manager(self):
with self.assertRaises(RuntimeError):
your_function()
def test_1b_raises_context_manager_and_error_message(self):
with self.assertRaises(RuntimeError) as error:
your_function()
self.assertEqual(str(error.exception), "your exception message")
def test_2_raises_oneliner(self):
self.assertRaises(RuntimeError, your_function)
def test_3_raises_regex_context_manager(self):
with self.assertRaisesRegex(RuntimeError, r'.* exception message'):
your_function()
def test_4_raises_regex_oneliner(self):
self.assertRaisesRegex(RuntimeError, r'.* exception message', your_function)
if __name__ == '__main__':
unittest.main()
Although it's up to developer which style to follow I prefer both methods using context manager.
You can use assertRaises from the unittest module:
import unittest
class TestClass():
def raises_exception(self):
raise Exception("test")
class MyTestCase(unittest.TestCase):
def test_if_method_raises_correct_exception(self):
test_class = TestClass()
# Note that you don’t use () when passing the method to assertRaises
self.assertRaises(Exception, test_class.raises_exception)
For those on Django, you can use context manager to run the faulty function and assert it raises the exception with a certain message using assertRaisesMessage
with self.assertRaisesMessage(SomeException,'Some error message e.g 404 Not Found'):
faulty_funtion()
For await/async aiounittest there is a slightly different pattern:
https://aiounittest.readthedocs.io/en/latest/asynctestcase.html#aiounittest.AsyncTestCase
async def test_await_async_fail(self):
with self.assertRaises(Exception) as e:
await async_one()
This will raise TypeError if setting stock_id to an Integer in this class will throw the error, the test will pass if this happens and fails otherwise
def set_string(prop, value):
if not isinstance(value, str):
raise TypeError("i told you i take strings only ")
return value
class BuyVolume(ndb.Model):
stock_id = ndb.StringProperty(validator=set_string)
from pytest import raises
buy_volume_instance: BuyVolume = BuyVolume()
with raises(TypeError):
buy_volume_instance.stock_id = 25
Unit testing with unittest would be preferred, but if you would like a quick fix, we can catch the exception, assign it to a variable, and see if that variable is an instance of that exception class.
Lets assume our bad function throws a ValueError.
try:
bad_function()
except ValueError as e:
assert isinstance(e, ValueError)
While all the answers are perfectly fine, I was looking for a way to test if a function raised an exception without relying on unit testing frameworks and having to write test classes.
I ended up writing the following:
def assert_error(e, x):
try:
e(x)
except:
return
raise AssertionError()
def failing_function(x):
raise ValueError()
def dummy_function(x):
return x
if __name__=="__main__":
assert_error(failing_function, 0)
assert_error(dummy_function, 0)
And it fails on the right line:
Traceback (most recent call last):
File "assert_error.py", line 16, in <module>
assert_error(dummy_function, 0)
File "assert_error.py", line 6, in assert_error
raise AssertionError()
AssertionError

Categories

Resources