How to detect when pytest test case failed? - python

I am using pytest with selenium to automate a website. I want to take some screen shot only when a test case fails. I have previosly used TestNG and with TestNG it's quite east using the ITestListner.
Do we have something like that in pytest.
I have tried to achieve this using the teardown_method()
But this method is not getting executed when a test case fails.
import sys
from unittestzero import Assert
class TestPY:
def setup_method(self, method):
print("in setup method")
print("executing " + method.__name__)
def teardown_method(self, method):
print(".....teardown")
if sys.exc_info()[0]:
test_method_name = method
print test_method_name
def test_failtest(self):
Assert.fail("failed test")
teardown_method() get executed only when there are no fails

According to you post on stackoverflow, I can share that I is something on my mind, I hope it will help:wink:
What you're trying to do is to handle standard AssertionError exception that can be raised by assert keyword or by any assertion method implemented in unittest.TestCase or maybe any custom assertion method that raises custom exception.
There are 3 ways to do that:
Use try-except-finally construction. Some basic example:
try:
Assert.fail("failed test")
except AssertionError:
get_screenshot()
raise
Or use with statement, as context manager:
class TestHandler:
def __enter__(self):
# maybe some set up is expected before assertion method call
pass
def __exit__(self, exc_type, exc_val, exc_tb):
# catch whether exception was raised
if isinstance(exc_val, AssertionError):
get_screenshot()
with TestHandler():
Assert.fail("failed test")
here you can dive deeper on how to play with it
The last one, in my opinion, is the most elegant approach. Using decorators. With this decorator you can decorate any testing method:
def decorator_screenshot(func):
def wrapper(*args, **kwargs):
try:
func(*args, **kwargs)
except AssertionError:
get_screenshot()
raise
return wrapper
#decorator_screenshot
def test_something():
Assert.fail("failed test")

After some struggle, eventually this worked for me.
In conftest.py:
#pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_makereport(item, call):
outcome = yield
rep = outcome.get_result()
setattr(item, "rep_" + rep.when, rep)
return rep
And, in your code, in a fixture (e.g., in a teardown fixture for tests) use it like so:
def tear_down(request):
method_name = request.node.name
if request.node.rep_call.failed:
print('test {} failed :('.format(method_name))
# do more stuff like take a selenium screenshot
Note that "request" is a fixture "funcarg" that pytest provides in the context of your tests. You don't have to define it yourself.
Sources: pytest examples and thread on (not) making this easier.

This is how we do it , note __multicall__ has very less documentation and I remember reading __multicall__ is going to be deprecated, please use this with a pinch of salt and experiment with replacing __multicall__ with 'item, call' as per the examples.
def pytest_runtest_makereport(__multicall__):
report = __multicall__.execute()
if report.when == 'call':
xfail = hasattr(report, 'wasxfail')
if (report.skipped and xfail) or (report.failed and not xfail):
try:
screenshot = APP_DRIVER.take_screen_shot(format="base64")
except Exception as e:
LOG.debug("Error saving screenshot !!")
LOG.debug(e)
return report

def pytest_runtest_makereport(item, call):
if call.when == 'call':
if call.excinfo is not None:
# if excinfor is not None, indicate that this test item is failed test case
error("Test Case: {}.{} Failed.".format(item.location[0], item.location[2]))
error("Error: \n{}".format(call.excinfo))

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

AssertError not catched in unittest in python try-except clause

I have a object created in a test case, and want to make test inside of its method.
But the exception is swallow by the try-except clause.
I know I can change raise the exception in run but it is not what I want. Is there a way that any unittest tool can handle this?
It seems that assertTrue method of unittest.TestCase is just a trivial assert clause.
class TestDemo(unittest.TestCase):
def test_a(self):
test_case = self
class NestedProc:
def method1(self):
print("flag show the method is running")
test_case.assertTrue(False)
def run(self):
try:
self.method1()
except:
pass # can raise here to give the exception but not what I want.
NestedProc().run() # no exception raised
# NestedProc().method1() # exception raised
EDIT
For clarity, I paste my realworld test case here. The most tricky thing here is that ParentProcess will always success leading to AssertError not correctly being propagated to test function.
class TestProcess(unittest.TestCase);
#pytest.mark.asyncio
async def test_process_stack_multiple(self):
"""
Run multiple and nested processes to make sure the process stack is always correct
"""
expect_true = []
def test_nested(process):
expect_true.append(process == Process.current())
class StackTest(plumpy.Process):
def run(self):
# TODO: unexpected behaviour here
# if assert error happend here not raise
# it will be handled by try except clause in process
# is there better way to handle this?
expect_true.append(self == Process.current())
test_nested(self)
class ParentProcess(plumpy.Process):
def run(self):
expect_true.append(self == Process.current())
proc = StackTest()
# launch the inner process
asyncio.ensure_future(proc.step_until_terminated())
to_run = []
for _ in range(100):
proc = ParentProcess()
to_run.append(proc)
await asyncio.gather(*[p.step_until_terminated() for p in to_run])
for proc in to_run:
self.assertEqual(plumpy.ProcessState.FINISHED, proc.state)
for res in expect_true:
self.assertTrue(res)
Any assert* method and even fail() just raises an exception. The easiest method is probably to manually set a flag and fail() afterwards:
def test_a(self):
success = True
class NestedProc:
def method1(self):
nonlocal success
success = False
raise Exception()
...
NestedProc().run()
if not success:
self.fail()

How can I stop the python process from terminating after a failed unit test so that I can call pdb.pm [duplicate]

Is there a way to automatically start the debugger at the point at which a unittest fails?
Right now I am just using pdb.set_trace() manually, but this is very tedious as I need to add it each time and take it out at the end.
For Example:
import unittest
class tests(unittest.TestCase):
def setUp(self):
pass
def test_trigger_pdb(self):
#this is the way I do it now
try:
assert 1==0
except AssertionError:
import pdb
pdb.set_trace()
def test_no_trigger(self):
#this is the way I would like to do it:
a=1
b=2
assert a==b
#magically, pdb would start here
#so that I could inspect the values of a and b
if __name__=='__main__':
#In the documentation the unittest.TestCase has a debug() method
#but I don't understand how to use it
#A=tests()
#A.debug(A)
unittest.main()
I think what you are looking for is nose. It works like a test runner for unittest.
You can drop into the debugger on errors, with the following command:
nosetests --pdb
import unittest
import sys
import pdb
import functools
import traceback
def debug_on(*exceptions):
if not exceptions:
exceptions = (AssertionError, )
def decorator(f):
#functools.wraps(f)
def wrapper(*args, **kwargs):
try:
return f(*args, **kwargs)
except exceptions:
info = sys.exc_info()
traceback.print_exception(*info)
pdb.post_mortem(info[2])
return wrapper
return decorator
class tests(unittest.TestCase):
#debug_on()
def test_trigger_pdb(self):
assert 1 == 0
I corrected the code to call post_mortem on the exception instead of set_trace.
Third party test framework enhancements generally seem to include the feature (nose and nose2 were already mentioned in other answers). Some more:
pytest supports it.
pytest --pdb
Or if you use absl-py's absltest instead of unittest module:
name_of_test.py --pdb_post_mortem
A simple option is to just run the tests without result collection and letting the first exception crash down the stack (for arbitrary post mortem handling) by e.g.
try: unittest.findTestCases(__main__).debug()
except:
pdb.post_mortem(sys.exc_info()[2])
Another option: Override unittest.TextTestResult's addError and addFailure in a debug test runner for immediate post_mortem debugging (before tearDown()) - or for collecting and handling errors & tracebacks in an advanced way.
(Doesn't require extra frameworks or an extra decorator for test methods)
Basic example:
import unittest, pdb
class TC(unittest.TestCase):
def testZeroDiv(self):
1 / 0
def debugTestRunner(post_mortem=None):
"""unittest runner doing post mortem debugging on failing tests"""
if post_mortem is None:
post_mortem = pdb.post_mortem
class DebugTestResult(unittest.TextTestResult):
def addError(self, test, err):
# called before tearDown()
traceback.print_exception(*err)
post_mortem(err[2])
super(DebugTestResult, self).addError(test, err)
def addFailure(self, test, err):
traceback.print_exception(*err)
post_mortem(err[2])
super(DebugTestResult, self).addFailure(test, err)
return unittest.TextTestRunner(resultclass=DebugTestResult)
if __name__ == '__main__':
##unittest.main()
unittest.main(testRunner=debugTestRunner())
##unittest.main(testRunner=debugTestRunner(pywin.debugger.post_mortem))
##unittest.findTestCases(__main__).debug()
To apply #cmcginty's answer to the successor nose 2 (recommended by nose available on Debian-based systems via apt-get install nose2), you can drop into the debugger on failures and errors by calling
nose2
in your test directory.
For this, you need to have a suitable .unittest.cfg in your home directory or unittest.cfg in the project directory; it needs to contain the lines
[debugger]
always-on = True
errors-only = False
To address the comment in your code "In the documentation the unittest.TestCase has a debug() method but I don't understand how to use it", you can do something like this:
suite = unittest.defaultTestLoader.loadTestsFromModule(sys.modules[__name__])
suite.debug()
Individual test cases are created like:
testCase = tests('test_trigger_pdb') (where tests is a sub-class of TestCase as per your example). And then you can do testCase.debug() to debug one case.
Here's a built-in, no extra modules, solution:
import unittest
import sys
import pdb
####################################
def ppdb(e=None):
"""conditional debugging
use with: `if ppdb(): pdb.set_trace()`
"""
return ppdb.enabled
ppdb.enabled = False
###################################
class SomeTest(unittest.TestCase):
def test_success(self):
try:
pass
except Exception, e:
if ppdb(): pdb.set_trace()
raise
def test_fail(self):
try:
res = 1/0
#note: a `nosetests --pdb` run will stop after any exception
#even one without try/except and ppdb() does not not modify that.
except Exception, e:
if ppdb(): pdb.set_trace()
raise
if __name__ == '__main__':
#conditional debugging, but not in nosetests
if "--pdb" in sys.argv:
print "pdb requested"
ppdb.enabled = not sys.argv[0].endswith("nosetests")
sys.argv.remove("--pdb")
unittest.main()
call it with python myunittest.py --pdb and it will halt. Otherwise it won't.
Some solution above modifies business logic:
try: # <-- new code
original_code() # <-- changed (indented)
except Exception as e: # <-- new code
pdb.post_mortem(...) # <-- new code
To minimize changes to the original code, we can define a function decorator, and simply decorate the function that's throwing:
def pm(func):
import functools, pdb
#functools.wraps(func)
def func2(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
pdb.post_mortem(e.__traceback__)
raise
return func2
Use:
#pm
def test_xxx(...):
...
Buildt a module with a decorator which post mortems into every type of error except AssertionError. The decorator can be triggered by the logging root level
#!/usr/bin/env python3
'''
Decorator for getting post mortem on errors of a unittest TestCase
'''
import sys
import pdb
import functools
import traceback
import logging
import unittest
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG)
def debug_on(log_level):
'''
Function decorator for post mortem debugging unittest functions.
Args:
log_level (int): logging levels coesponding to logging stl module
Usecase:
class tests(unittest.TestCase):
#debug_on(logging.root.level)
def test_trigger_pdb(self):
assert 1 == 0
'''
def decorator(f):
#functools.wraps(f)
def wrapper(*args, **kwargs):
try:
return f(*args, **kwargs)
except BaseException as err:
info = sys.exc_info()
traceback.print_exception(*info)
if log_level < logging.INFO and type(err) != AssertionError:
pdb.post_mortem(info[2])
return wrapper
return decorator
class Debug_onTester(unittest.TestCase):
#debug_on(logging.root.level)
def test_trigger_pdb(self):
assert 1 == 0
if __name__ == '__main__':
unittest.main()

Take screenshot on a test assert error / exception

I am running my ui tests with Python + pytest + selenium.
I need to do a screenshot on any test fail (any exception / assertion error etc.)
I would like to impement it in my BaseEnvironment class, which looks like this now
#pytest.mark.flaky(max_runs=3, min_passes=1)
class Rerun:
pass
class BaseEnvironment(Rerun):
#classmethod
def setup_class(cls):
warnings.simplefilter("ignore")
cls.driver = create_webdriver()
#classmethod
def teardown_class(cls):
cls.driver.close()
cls.driver.quit()
I found a lot of solutions with google but its really complicated or for pyrhon 2.x. I need something simple, like decorator. It must apply to all my tests.
But not something like add to setup test_status = False, on the end of each test test_status = True and check it in teardown. I would like to do it clean, if it possible.
Thanks in advice!
I was looking to do this too, the above answer was a good start but I ended up using something slightly different:
def screenshot_dec(func):
def wrapper(selenium):
try:
func(selenium)
except:
selenium.save_screenshot("error_{0}.png".format(func.__name__))
raise
return wrapper
#screenshot_dec
def test_my_test(selenium):
# test code here where selenium is my driver instance
This generates error_test_mytest.png on any exception.
Here is a decorator that should take a screenshot after each exception and failed assertion which I believe is what you want. Try something like the following?
def decorator_screenshot(func):
def wrapper(func, *args, **kwargs):
try:
return func(*args, **kwargs)
except:
return get_screenshot()
return wrapper
#decorator_screenshot
def test_something():
Assert.fail("failed test")

Categories

Resources