Python assertRaises on user-defined exceptions - python

The following question was triggered by the discussion in this post.
Assume two files (foobar.py and foobar_unittest.py). File foobar.py contains a class (FooBar) with two functions (foo and bar). Function bar raises a built-in exception, function foo a user-defined exception.
# foobar.py
class MyException(Exception):
pass
class FooBar:
def __init__(self):
pass
def bar(self):
raise ValueError('Hello World.')
def foo(self):
raise MyException('Hello World.')
.
# foobar_unittest.py
import unittest
import foobar as fb
class MyException(Exception):
pass
class FooBarTestCases(unittest.TestCase):
def test_bar(self):
with self.assertRaises(ValueError):
fb.FooBar().bar()
def test_foo(self):
with self.assertRaises(MyException):
fb.FooBar().foo()
if __name__ == '__main__':
unittest.main()
When running unit-test on foobar.py, why does the function raising the user-defined exception (foo) fail to pass the test?
>>> python2.7 foobar_unittest.py
.E
======================================================================
ERROR: test_foo (__main__.FooBarTestCases)
----------------------------------------------------------------------
Traceback (most recent call last):
File "foobar_unittest.py", line 11, in test_foo
fb.FooBar().foo()
File "/a_path/foobar.py", line 9, in foo
raise MyException('Hello World.')
MyException: Hello World.
----------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (errors=1)

import MyException from foobar, don't redefine it.
import unittest
from foobar import MyException
import foobar as fb
class FooBarTestCases(unittest.TestCase):
def test_bar(self):
with self.assertRaises(ValueError):
fb.FooBar().bar()
def test_foo(self):
with self.assertRaises(MyException):
fb.FooBar().foo()
if __name__ == '__main__':
unittest.main()
This code should work now as
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK

Be aware that the same happens (happened to me) if you use reload to import your exceptions.
In my unittests I have the relevant imports like
from importlib import reload
import foobar
reload(foobar)
from foobar import MyException
This does not work, too, for whatever reason. Writing this just as
from foobar import MyException
will work. Then, of course, you have to reload the modules yourself.
In case you wonder why I am using reload: How do I unload (reload) a Python module?.

Related

Why I cannot effectively mock function with unittest.mock.patch?

I'm trying to write UT for function with other function mocked. Both lies in the same module. It appears that unittest.mock.patch() call passes without error but doesn't mock anything so I'm getting exception in the example code. So, my question is: How to mock foo with unittest.mock.patch ?
import unittest
import unittest.mock
def foo():
raise Exception("not implemented")
def bar():
foo()
class UT(unittest.TestCase):
def testBar(self):
with unittest.mock.patch('mock_test.foo',spec=True) as mock_foo:
bar() # <- original foo is called here
unittest.main()
Here is a way to use unittest.mock.patch and patch the foo returns. See the comments in the example:
import unittest
from unittest.mock import patch
def foo():
raise Exception("Not implemented")
def bar():
return foo()
class UT(unittest.TestCase):
def testBar(self):
# foo is mocked to raise a ValueError and catch it during the test
with patch('__main__.foo', side_effect=ValueError):
with self.assertRaises(ValueError):
bar()
# foo is mocked to raise and exception and catch it during the test
with patch('__main__.foo', side_effect=Exception):
with self.assertRaises(Exception):
bar()
# foo is mocked to return 1
with patch('__main__.foo', return_value=1):
self.assertEqual(bar(), 1, "Should be equal to 1")
# foo is mocked to return an object
with patch('__main__.foo', return_value={}):
self.assertIsInstance(bar(), dict, "Should be an instance of dict")
unittest.main()
Output:
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
For more informations, please visit the official documentation

Skipping unittest with decorator in python

I'm writing some unittest and found a rather curious behavior that nearly burned me.
The following test:
import unittest
class Test(unittest.TestCase):
#unittest.skip('Not ready yet')
def test_A(self):
self.assertTrue(False)
#unittest.skip
def test_B(self):
self.assertTrue(False)
def test_C(self):
self.assertTrue(False)
if __name__ == '__main__':
unittest.main()
results in:
test_A (__main__.Test) ... skipped 'Not ready yet'
test_B (__main__.Test) ... ok
test_C (__main__.Test) ... FAIL
======================================================================
FAIL: test_C (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "./test.py", line 13, in test_C
self.assertTrue(False)
AssertionError: False is not true
----------------------------------------------------------------------
Ran 3 tests in 0.000s
Using the decorator unittest.skip empty does skip the test but then reports it as passed. Therefore this skipped test could be easily forgotten the next day and stay in the skip state forever. What is the reason behind this skip, but report pass behavior?
In case it matters:
Python: 3.4.3 | Anaconda 2.3.0 (64-bit)
OS: RHEL 6.7
#decorator
def f(): ...
is equivalent to
def f(): ...
f = decorator(f)
and
#decorator(...)
def f(): ...
is equivalent to
def f(): ...
f = decorator(...)(f)
That means when you forget the skip reason, you get the effects of
def test_B(self): ...
test_B = unittest.skip(test_B)
The test method is passed as the skip reason, and the returned test decorator is assigned to test_B. When unittest tries to run test_B, the test decorator reports no assertion failures, so unittest thinks it's a passing test.
The inequivalence of #decorator and #decorator() is one of Python's design warts, but there isn't much we can do about it.

Python mocked exception not being caught

Struggling to succinctly describe this in the title...
I have a module I want to test:
mod.py:
import subprocess
class MyStuff(object):
def my_fun(self):
try:
print subprocess
out = subprocess.check_output(["echo", "pirates"])
except subprocess.CalledProcessError:
print "caught exception"
And the test module test_mod.py:
import unittest
import mock
from mod import MyStuff
import subprocess
class Tests(unittest.TestCase):
def setUp(self):
self.patched_subprocess = mock.patch(
'mod.subprocess', autospec=True)
self.mock_subprocess = self.patched_subprocess.start()
self.my_stuff = MyStuff()
def tearDown(self):
self.patched_subprocess.stop()
def test_my_fun(self):
self.mock_subprocess.check_output = mock.Mock(
side_effect=subprocess.CalledProcessError(0, "hi", "no"))
with self.assertRaises(subprocess.CalledProcessError):
out = self.my_stuff.my_fun()
if __name__ == '__main__':
unittest.main()
I then run python test_mod.py and I see the following output:
<NonCallableMagicMock name='subprocess' spec='module' id='140654009377872'>
.
----------------------------------------------------------------------
Ran 1 test in 0.007s
OK
I'm pleased that the subprocess object has been mocked, but why is the print "caught exception" statement not executed? I'm guessing it's because the real exception getting throw in test_mod.subprocess.CalledProcessException and not subprocess.CalledProcessException as I intend, but I'm not sure how to resolve that. Any suggestion? Thanks for your time.
I solved this eventually...
The problem was I was mocking the entire subprocess module, which included the CalledProcessError exception! That's why it didn't seem to match the exception I was raising in my test module, because it was a completely different object.
The fix is to mock just subprocess.check_output, D'oh!

Why python mock patch doesn't work?

I have two files
spike.py
class T1(object):
def foo(self, afd):
return "foo"
def get_foo(self):
return self.foo(1)
def bar():
return "bar"
test_spike.py:
from unittest import TestCase
import unittest
from mock import patch, MagicMock
from spike import T1, bar
class TestStuff(TestCase):
#patch('spike.T1.foo', MagicMock(return_value='patched'))
def test_foo(self):
foo = T1().get_foo()
self.assertEqual('patched', foo)
#patch('spike.bar')
def test_bar(self, mock_obj):
mock_obj.return_value = 'patched'
bar = bar()
self.assertEqual('patched', bar)
if __name__ == "__main__":
unittest.main()
When I run python test_spike.py, the first test case would pass, but the second would fail.
and I switch to use nosetests test_spike.py, then both two are failed.
I don't understand how this happened? These cases supposed to pass all.
Access bar using spike.bar. Imported bar is not affected by mock.patch.
from unittest import TestCase
import unittest
from mock import patch, MagicMock
from spike import T1
import spike # <----
class TestShit(TestCase):
#patch('spike.T1.foo', MagicMock(return_value='patched'))
def test_foo(self):
foo = T1().get_foo()
self.assertEqual('patched', foo)
#patch('spike.bar')
def test_bar(self, mock_obj):
mock_obj.return_value = 'patched'
bar = spike.bar() # <-----
self.assertEqual('patched', bar)
if __name__ == "__main__":
unittest.main()
For test_foo you are not using patch correctly. You should be using it like this:
class TestFoo(TestCase):
#patch.object(T1, 'foo', MagicMock(return_value='patched'))
def test_foo(self):
foo = T1().get_foo()
self.assertEqual('patched', foo)
that gives me:
nosetests test_spike.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Now the second example does not work because you import bar function (get a reference to it) and then try to mock it. When you mock something you can't change what your variables hold (reference to original function). To fix this you should use #falsetru suggested method like:
from unittest import TestCase
import unittest
from mock import patch
import spike
class TestFoo(TestCase):
#patch('spike.bar')
def test_bar(self, mock_obj):
mock_obj.return_value = 'patched'
value = spike.bar()
self.assertEqual('patched', value)
if __name__ == "__main__":
unittest.main()
this gives me:
python test_spike.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
But when I try to run it with nose I get:
nosetests test_spike.py
F
======================================================================
FAIL: test_bar (src.test_spike.TestFoo)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/zilva/envs/test/local/lib/python2.7/site-packages/mock/mock.py", line 1305, in patched
return func(*args, **keywargs)
File "/home/zilva/git/test/src/test_spike.py", line 11, in test_bar
self.assertEqual('patched', value)
AssertionError: 'patched' != 'bar'
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)
This happends because I am patching not in the right place. My directory structure is:
test/
└── src/
├── spike.py
├── test_spike.py
└── __init__.py
and I run tests from src directory so I should be patching using path from project root directory like:
#patch('src.spike.bar')
and this would give me:
nosetests test_spike.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
or if I am at test directory:
nosetests src/test_spike.py
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
To elaborate on the very helpful top answer, let me paraphrase the official documentation for unittest.mock.
a.py
class SomeClass:
...
b.py
import a
from a import SomeClass
def some_function():
a.SomeClass()
SomeClass()
If you write mock.patch('a.SomeClass'), this will affect the first line of some_function. If you write mock.patch('b.SomeClass'), this will affect the second line.

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