Unit test object construction that raises ValueError - python

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.

Related

how to write Unittets for a method which is returning a method

def acquisition_required(method):
def wrapped_method(self, *args, **kwargs):
result=some complex code
if not result:
some code is here
else:
return method(self, *args, **kwargs)
return wrapped_method
I would like to write a Unittest for this
eg.
assertEqual, assertTrue..
But i don't know how to test it i have done unittesting for function returning some values or True/False.
I don't want any code just concept
If your function will be success then the return value will be the reference of getting method. It means you can use the assertEqual method of unittest module. Like below:
import unittest
import your_module
class ConfigParserTestCases(unittest.TestCase):
def test_return_method(self):
self.assertEqual(your_module.acquisition_required(method), method)
if __name__ == "__main__":
unittest.main()
Note: If your implementation contains Exceptions (Eg.: try-except for error handling), you can test the Exception case with "assertRaises" method of unittest module. From the Official Python documentation.
Eg. in your case (if you want to test ValueError exception):
with self.assertRaises(ValueError):
self.assertRaises(your_module.acquisition_required(method))
I manged to do this as below:
with self.assertRaises(ValueError):
self.assertRaises(your_module.acquisition_required(method))

Unittest for IOError exception handling

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)

Python unittest testcase on catching error failing

So I have class Dog:
class Dog:
def __init__(self, res):
self._res = res
def error(self):
raise IndexError
and unit test:
class TestDog(unittest.TestCase):
def setUp(self):
self.dog = Dog([1,2])
def testError(self):
self.assertRaises(IndexError, self.dog.error())
Why is the testcase testError failing? self.dog.error() should result in IndexError being rasied, and I believe the assertRaises is correctly formatted.
Any help is appreciated, thanks.
When Python runs the line self.assertRaises(IndexError, self.dog.error()), it has to calculate the arguments of the method self.assertRaises before passing them to it, so it calls self.dog.error() which raises an error. This is how all functions work. The presence of self.assertRaises does nothing to stop the error being raised. You are meant to call self.assertRaises(IndexError, self.dog.error), where the second argument is now the actual function/method object self.dog.error without having called it yet. self.assertRaises will call it for you wrapped in a try/except so that it can do the test properly.
Alternatively:
with self.assertRaises(IndexError):
self.dog.error()

Mocking the super class calls on python

I am doing some unit testing and at some point I need to mock a super call to throw an error, for example:
#classmethod
def myfunc(cls, *args, **kwargs)
try:
super(MyClass, cls).my_function(args, kwargs)
except MyException as e:
#...
I am using the mocker library to mock my objects in general but I haven't found a way to mock this.
Using unittest.mock from the standard library I would do something like this.
In your class definition:
from somelib import ASuperClass
class MyClass(ASuperClass):
def my_cool_method(self):
return super().my_cool_method()
In the module where you are calling MyClass:
from unittest.mock import patch
from mymodule import MyClass
#patch("mypackage.mymodule.ASuperClass.my_cool_method")
def call_with_mock(mocked_super):
myinstance = MyClass()
myinstance.my_cool_method()
# do stuff with `mocked_super`
call_with_mock()
I found a way, sort of hacky but it works, I'll explain with my example, this is based on this response so thanks #kindall:
def my_test(self):
import __builtin__
from mocker import Mocker, KWARGS, ARGS
mymocker = mocker.mock()
mymocker.my_function(ARGS, KWARGS)
mocker.throw(MyException)
def mysuper(*args, **kwargs):
if args and issubclass(MyClass, args[0]):
return mymocker
return original_super(*args, **kwargs)
__builtin__.original_super = super
__builtin__.super = mysuper
with mocker:
MyClass.myfunc()
so essentially what I do is check if the super call is from the class I want to mock, else just do a normal super.
Hope this helps someone :)
In case anyone needs another way to solve this mock:
# some_package/some_module.py
class MyClass(SuperClass):
def some_function(self):
result_super_call = super().function()
# test_file.py
#patch('some_package.some_module.super')
def test_something(self, mock_super):
obj = MyClass()
mock_super().some_function.return_value = None
Using Python 3.6
#Markus is looking in the right place. So long as you're unit testing (i.e. there's only one call to super), you can mock __builtin__.super as in:
with mock.patch('__builtin__.super') as mock_super:
mock_super.side_effect = TypeError
with self.assertRaises(TypeError):
obj.call_with_super()
Python's own Mock class provides a spec argument that should help with that:
with mock.patch('...ParentClass.myfunc') as mocked_fn:
mocked_fn.side_effect = MyException() # Parent's method will raise
instance = mock.Mock(spec=MyClass) # Enables using super()
MyClass.myfunc(instance) # Will enter your `except` block
Well, then you need to mock the my_function method of the superclass of MyClass to blow up.

How to fail a python unittest if the setUpClass throws exception

I am having little trouble using the python setUpClass.
For example consider the following case
class MyTest(unittest.case.TestCase):
#classmethod
def setUpClass(cls):
print "Test setup"
try:
1/0
except:
raise
#classmethod
def tearDownClass(cls):
print "Test teardown"
A couple of questions
Is the above code the right way to handle test setUpClass exceptions (by raising it so that the python unittest can take care of it), there are fail(), skip() methods, but those can only be used by test instances and not the test classes.
When there is a setUpClass exception, how can we ensure that tearDownClass runs (unittest doesn't run it, should we manualy call it).
You can call tearDownClass on an exception as Jeff points it out, but you may also implements the __del__(cls) method :
import unittest
class MyTest(unittest.case.TestCase):
#classmethod
def setUpClass(cls):
print "Test setup"
try:
1/0
except:
raise
#classmethod
def __del__(cls):
print "Test teardown"
def test_hello(cls):
print "Hello"
if __name__ == '__main__':
unittest.main()
Will have the following output :
Test setup
E
======================================================================
ERROR: setUpClass (__main__.MyTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "my_test.py", line 8, in setUpClass
1/0
ZeroDivisionError: integer division or modulo by zero
----------------------------------------------------------------------
Ran 0 tests in 0.000s
FAILED (errors=1)
Test teardown
Note : you should be aware that the __del__ method will be called at the end of the program execution, which is maybe not what you want if you have a more than one test class.
Hope it helps
The best option would be is to add handler for the except which calls tearDownClass and re-raise exception.
#classmethod
def setUpClass(cls):
try:
super(MyTest, cls).setUpClass()
# setup routine...
except Exception: # pylint: disable = W0703
super(MyTest, cls).tearDownClass()
raise
import contextlib
class MyTestCase(unitest.TestCase):
#classmethod
def setUpClass(cls):
with contextlib.ExitStack() as stack:
# ensure teardown is called if error occurs
stack.callback(cls.tearDownClass)
# Do the things here!
# remove callback at the end if no error found
stack.pop_all()
use tearDownModule. It should be called after setUpClass runs.

Categories