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.

here's the traceback

Was it helpful?

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/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top