How to disable decorator during project unittests in Python?
When running the unittests, I would like to disable the selected decorator for the duration of the tests.
import unittest
import functools
def handle_value_error(func):
#functools.wraps(func)
def wrapper_handle_value_error():
try:
return func()
except ValueError as e:
print(f"During calling {func.__name__!r} an error was raised: {str(e)}")
return wrapper_handle_value_error
#handle_value_error
def func_that_raise_value_error():
# do something
raise ValueError
def disable_decorator_for(func, decorator):
# do something that disable handle_value_error decorator
pass
class TestFunc(unittest.TestCase):
def test_func_that_raise_value_error(self):
disable_decorator_for(func=func_thar_raise_value_error, decorator=handle_value_error)
with self.assertRaises(ValueError):
func_thar_raise_value_error()
When function disable_decorator_for() is called, the tests should pass, but the output is:
During calling 'func_thar_raise_value_error' an error was raised:
Failure
Traceback (most recent call last):
File "...\disable_decorator_for_unittests.py", line 29, in test_func_thar_raise_value_error
with self.assertRaises(ValueError):
AssertionError: ValueError not raised
Ran 1 test in 0.003s
FAILED (failures=1)
Process finished with exit code 1
Related
I wrote this test, but in order not to delay the test, I mock the time.sleep and the test will encounter an fail.
from unittest.mock import patch
from django.core.management import call_command
from django.db.utils import OperationalError
from django.test import TestCase
class CommandsTest(TestCase):
#patch('time.sleep', return_value=None)
def test_wait_for_db(self):
"""Test waiting for db"""
with patch('django.utils.connection.BaseConnectionHandler.__getitem__') as gi:
gi.side_effect = [OperationalError] * 5 + [True]
call_command('wait_for_db')
self.assertEqual(gi.call_count, 6)
By commenting on this second line(#patch), the program will run properly.
here is the error:
ERROR: test_wait_for_db (core.tests.test_commands.CommandsTest)
Test waiting for db
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/local/lib/python3.10/unittest/mock.py", line 1369, in patched
return func(*newargs, **newkeywargs)
TypeError: CommandsTest.test_wait_for_db() takes 1 positional argument but 2 were given
You'll need to add an argument to your test_wait_for_db.
Since you use a decorator, the mocked function is passed as argument to that function
class CommandsTest(TestCase):
#patch('time.sleep', return_value=None)
def test_wait_for_db(self, mocked_sleep):
In your test you can then test assert if indeed was called. More information here.
I have a dummy funtion to try exceptions:
def fun(n):
try:
if n <1:
raise ValueError
return 1
except:
pass
In my unit test I use:
import unittest
class TestFibonnacci(unittest.TestCase):
def test_values(self):
self.assertEqual(fun(1),1)
self.assertRaises(ValueError, fun(-1))
However I'm unable to get an answer I actually get:
An exception occurred
E....
ERROR: test_values (main.TestFibonnacci)
Traceback (most recent call last):
The traceback here
TypeError: 'NoneType' object is not callable
Ran 1 tests in 0.001s
What I'm doing wrong?
You are calling fun(-1) immediately, rather than letting self.assertRaises call it, so that it can catch the exception.
You have to pass the function and its arguments separately.
self.assertRaises(ValueError, fun, -1)
Alternatively, you can use assertRaises as a context manager.
with self.assertRaises(ValueError):
fun(-1)
The with statement captures the exception raised by fun and provides it to the __exit__ method of the value returned by assertRaises.
What is wrong with my code below?
I'm expecting assert call_func_once_with("b") to throw an error as call_func was passed 'a'. I confimed that the function was indeed called once and with argument 'a'.
from unittest.mock import Mock, patch
def call_func(x):
pass
#patch("__main__.call_func")
def test_call_func(call_func):
call_func("a")
assert call_func.called_once_with("b")
assert call_func.called == 1
print(call_func.call_args)
test_call_func()
Output:
call('a')
You're not the first person to notice strange things with these types of assertions (see Magic mock assert_called_once vs assert_called_once_with weird behaviour)
For what it's worth, I can only advise that you try to create a test class which inherits from unittest.TestCase and then use the assertEqual method to get more consistent test behaviour:
import unittest
from unittest.mock import patch, call
def call_func(x):
pass
class MyTests(unittest.TestCase):
#patch("__main__.call_func")
def test_call_func(self, call_func_mock):
call_func_mock("a")
# assert call_func_mock.called == 1
# assert call_func_mock.called_once_with("b")
self.assertEqual(call_func_mock.call_count, 1)
self.assertEqual(call_func_mock.call_args_list[0], call("b"))
print(call_func_mock.call_args)
unittest.main()
This gives the following (expected) results:
F
======================================================================
FAIL: test_call_func (__main__.MyTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Python36\lib\unittest\mock.py", line 1179, in patched
return func(*args, **keywargs)
File "C:/scratch.py", line 16, in test_call_func
self.assertEquals(call_func_mock.call_args_list[0], call("b"))
AssertionError: call('a') != call('b')
----------------------------------------------------------------------
Ran 1 test in 0.003s
FAILED (failures=1)
Process finished with exit code 1
This question already has answers here:
How do you test that a Python function throws an exception?
(19 answers)
Closed 4 years ago.
Can somebody tell me why the following unit-test is failing on the
ValueError in test_bad, rather than catching it with assertRaises
and succeeding? I think I'm using the correct procedure and syntax,
but the ValueError is not getting caught.
I'm using Python 2.7.5 on a linux box.
Here is the code …
import unittest
class IsOne(object):
def __init__(self):
pass
def is_one(self, i):
if (i != 1):
raise ValueError
class IsOne_test(unittest.TestCase):
def setUp(self):
self.isone = IsOne()
def test_good(self):
self.isone.is_one(1)
self.assertTrue(True)
def test_bad(self):
self.assertRaises(ValueError, self.isone.is_one(2))
if __name__ == "__main__":
unittest.main()
and here is the output of the unit-test:
======================================================================
ERROR: test_bad (__main__.IsOne_test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test/raises.py", line 20, in test_bad
self.assertRaises(ValueError, self.isone.is_one(2))
File "test/raises.py", line 8, in is_one
raise ValueError
ValueError
----------------------------------------------------------------------
Ran 2 tests in 0.008s
FAILED (errors=1)
Unittest's assertRaises takes a callable and arguments, so in your case, you'd call it like:
self.assertRaises(ValueError, self.isone.is_one, 2)
If you prefer, as of Python2.7, you could also use it as a context manager like:
with self.assertRaises(ValueError):
self.isone.is_one(2)
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.