Unittest for IOError exception handling - python

Given this code:
try:
#do something
except IOError as message:
logging.error(message)
raise message
I want to test the exception handling part in order to have full coverage.
In the unittest I've tried with:
with patch(new=Mock(side_effect=IOError(errno.EIO))):
self.assertRaises(IOError)
but it doesnt work.
Is this approach correct?

Actually you need to start the Mock so that the side_effectstarts, for example the following:
class Test(unittest.TestCase):
def test(self):
mock = m.Mock()
mock.side_effect = Exception("Big badaboum")
self.assertRaises(Exception, mock)
self.assertRaises can take a callable as second argument, making it equivalent to:
class Test(unittest.TestCase):
def test(self):
mock = m.Mock()
mock.side_effect = Exception("Big badaboum")
with self.assertRaises(Exception):
mock()
And if you want to use it in a test with patch, you can do the following:
import unittest.mock as m
import unittest
def raise_error():
try:
print("Hello") #placeholder for the try clause
except Exception as e:
print(e) #placeholder for the exceptclause
class Test(unittest.TestCase):
#m.patch("__main__.raise_error", side_effect=Exception("Big badaboum")) #replace __main__ by the name of the module with your function
def test(self, mock):
with self.assertRaises(Exception):
mock()
unittest.main()
Edit: And to test the raise of an error inside an except block you need to mock a function call inside the try block you wrote, for instance:
import unittest.mock as m
import unittest
def do_sthing():
print("Hello")
def raise_error():
try:
do_sthing() #this call can be mocked to raise an IOError
except IOError as e:
print(e.strerror)
raise ValueError("Another one")
class Test(unittest.TestCase):
def test(self):
with m.patch("__main__.do_sthing", side_effect=IOError("IOError")):
self.assertRaises(ValueError, raise_error)
unittest.main()
You can use the decorator syntax as well (just putting the test above rewritten to spare some CPU cycle):
class Test(unittest.TestCase):
#m.patch("__main__.do_sthing",side_effect=IOError("IOError"))
def test(self, mock):
self.assertRaises(ValueError, raise_error)

Related

Unit test object construction that raises ValueError

I have a class which raises ValueError for a non-existent file path constructor parameter and I'd like to test it.
import os
import os.path
class MetricsManager:
def __init__(self, reference_file_path, translations_file_path):
if not os.path.isfile(reference_file_path):
raise ValueError(f'{reference_file_path} does not exist.')
if not os.path.isfile(translations_file_path):
raise ValueError(f'{translations_file_path} does not exist')
self._references_file_path = reference_file_path
self.translations_file_path = translations_file_path
But my test below fails.
import unittest
from src.metrics_manager import MetricsManager
def create_instance():
x = MetricsManager('foo.txt', 'bar.txt')
class MyTestCase(unittest.TestCase):
def test_something(self):
self.assertRaises(ValueError, create_instance())
if __name__ == '__main__':
unittest.main()
I did not have the assertRaises() call coded properly:
self.assertRaises(ValueError, MetricsManager, 'foo.txt', 'bar.txt')
assertRaises takes the function to be called as an argument, not the return value from actually calling it.
class MyTestCase(unittest.TestCase):
def test_something(self):
self.assertRaises(ValueError, create_instance)
If you call it first, the exception occurs before assertRaises gets called. You have to let assertRaises make the actual call.
You can also use assertRaises in a with statement:
class MyTestCase(unittest.TestCase):
def test_something(self):
with self.assertRaises(ValueError):
create_instance()
Here, you do call create_instance yourself, because any exception raised inside the with statement is implicitly caught and passed to the context manager's __exit__ method.

how to skip the all testcase when setup_class raise exception

does any know how can I skip the testsuite if there is a exception occurs on setup_class? I have the code like following
import pytest
##pytest.mark.skip(reason='skip if testsuite') #i can put skip here to skip all TestSuite
class TestSuite():
#classmethod
def setup_class(cls):
try:
raise Exception(f'can not find suitable strategy: {e}')
except Exception as a:
#I want to skip the testsuite for all testcase
def test_func1(self):
pass
def test_func2(self):
pass
def test_func3(self):
pass
http://doc.pytest.org/en/latest/skipping.html
You could put this in your exception block:
pytest.skip("failed to complete setup")

Mock a exception outside the scope of class Exception with Python

****** # I want to mock an exception beyond the scope of class Exception in Python. Below is my not-functional code. Please help identifying where's wrong. ******
from mock import patch
import unittest2
class A():
pass
class B(object):
def a(self):
return True
def b(self):
***# This function runs without problem and created an error outside of class Exception***
try:
raise A()
except Exception as ex:
print type(ex).__name__
except:
print 'error outside of Exception is captured.'
def c(self):
# This is for test
try:
self.a()
except Exception as ex:
print type(ex).__name__
except:
print 'error outside of Exception is captured.'
class Test(unittest2)
def raise_error(self):
raise A()
#patch('B.a')
def test_c(self, mock_a):
***# This does not function as B().b(), instead it throws an TypeError Exception. I need to mock an error not inherited from class Exception ***
mock_a.side_effect = self.raise_error
B().c()

mock - patch class method to raise exception

Here's a generalized example of the code I'm trying to test using mock. I get an AttributeError.
Here's __init__.py:
import logging
log = logging.getLogger(__name__)
class SomeError(Exception):
pass
class Stuff(object):
# stub
def method_a(self):
try:
stuff = self.method_b()
except SomeError, e:
log.error(e)
# show a user friendly error message
return 'no ponies'
return 'omg ponies'
def method_b(self):
# can raise SomeError
return ''
In tests.py I have something like this:
import mock
import unittest
from package.errors import SomeError
from mypackage import Stuff
some_error_mock = mock.Mock()
some_error_mock.side_effect = SomeError
class MyTest(unittest.TestCase):
#mock.patch.object('Stuff', 'method_b', some_error_mock)
def test_some_error(self):
# assert that method_a handles SomeError correctly
mystuff = Stuff()
a = mystuff.method_a()
self.assertTrue(a == 'no ponies')
When running the test, mock raises AttributeError saying: "Stuff does not have the attribute 'method_b'"
What am I doing wrong here?
The decorator first argument should be the class of the object, you mistakenly used a string of the name of the class
#mock.patch.object('Stuff', 'method_b', some_error_mock)
should become
#mock.patch.object(Stuff, 'method_b', some_error_mock)
http://docs.python.org/dev/library/unittest.mock#patch-object

Python Unit Testing: Automatically Running the Debugger when a test fails

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()

Categories