Patching a function with Mock only for one module?
-
04-07-2021 - |
Question
I need to patch os.listdir
and other os
functions to test my Python function. But when they're patched, the import
statement fails. Is it possible to patch this function only inside that single module, the real one, and leave tests.py work normally?
Here's an example that breaks import
:
import os
from mock import patch
# when both isdir and isfile are patched
# the function crashes
@patch('os.path.isdir', return_value=False)
@patch('os.path.isfile', return_value=False)
def test(*args):
import ipdb; ipdb.set_trace()
real_function(some_arguments)
pass
test()
I want real_function
to see a patched os.path
, and tests to see normal functions.
Solution
You can use patch
as a context manager, so it will only apply to the code inside the with
statement:
import os
from mock import patch
def test(*args):
import ipdb; ipdb.set_trace()
with patch('os.path.isdir', return_value=False):
with patch('os.path.isfile', return_value=False):
real_function(some_arguments)
pass
test()
OTHER TIPS
The below works for what you need. Notice I print os.listdir before and after the test run just to show everything is returned to it's correct state.
import unittest, os
from unittest import TestLoader, TextTestRunner
def tested_func(path):
return os.listdir(path * 2)
class Example(unittest.TestCase):
def setUp(self):
self._listdir = os.listdir
os.listdir = lambda p: ['a', 'b', 'a', 'b']
def test_tested_func(self):
self.assertEqual(tested_func('some_path'), ['a', 'b', 'a', 'b'])
def tearDown(self):
os.listdir = self._listdir
print os.listdir
TextTestRunner().run(TestLoader().loadTestsFromTestCase(Example))
print os.listdir
You can use patch as a decorator on either test methods or test classes. It makes for very clean looking code. Notice that the patched object is passed into the test method. You can do the same thing at the test class level and the mock object will be passed into every test method. There is no need to use setUp or tearDown in this case because that is all handled automatically.
# module.py
import os
def tested_func(path):
return os.listdir(path * 2)
# test.py
from mock import patch
@patch('module.os.listdir')
def test_tested_func(self, mock_listdir):
mock_listdir.return_value = ['a', 'b']
self.assertEqual(tested_func('some_path'), ['a', 'b', 'a', 'b'])
mock_listdir.assert_called_with('some_path' * 2)
Also notice that you mock the function you want to patch in the module that you want to actually test.
You not only "can", but you "should" do that http://mock.readthedocs.org/en/latest/patch.html#where-to-patch
p.s.: If you want to do more "declarative" way to describe unit's side_effects that you want to patch, I recommend using library http://mockstar.readthedocs.org/