How to mock using patch relative paths?

Question:

I have something like this in a python test file:

from mock import patch,
from ..monkey import ook
[...]
@patch('monkey.ook', Mock(return_value=None))
def test_run_ook (self, mock_ook):
    self.assertIsNone(ook())
    mock_ook.run.assert_called_once_with('')

When I run this test, I get a ImportError: No module named monkey. Clearly, the path I am patching is not right. However, I am not sure how to make it right without messing with sys.path or PYTHONPATH.

Any pointers?

Answers:

Use the complete path for the import. For example, if you have this filesystem:

  • root/
    • dumy/
      • foo/
        • module.py
    • dummy2/
      • module2.py

You can import module.py from module2.py using:

from root.dummy.foo import module
Answered By: jvallver

I think this comes from the fact that you don’t import monkey but instead import ook. If you import monkey from .. then it should work. Otherwise, just call patch on ook.

Not sure if this is the best way (or even recommended), but one way is to use something like:

from mock import patch,
from ..monkey import ook
[...]

package_base = __package__.rsplit('.', 1)[0]

@patch('{0}.monkey.ook'.format(package_base), Mock(return_value=None))
def test_run_ook (self, mock_ook):
    self.assertIsNone(ook())
    mock_ook.run.assert_called_once_with('')
Answered By: jerrykan

From what I gather, with mock, you need to provide a dotted name when patching. Luckily, every module has access to a special module-level variable __name__ which contains the module’s name. Using this, if you want to patch variables local to your module, you should be able to do something like the following:

import mock
import unittest

ook = lambda: "the ook"


class OokTest(unittest.TestCase):

    def test_ook(self):
        with mock.patch(__name__ + '.ook', return_value=None):
            self.assertIsNone(ook())
        self.assertEquals(ook(), "the ook")

    # the patch decorator should work the same way, I just tend to use the
    # context manager out of personal preference
    @mock.patch(__name__ + '.ook', return_value=None)
    def test_ook_2(self, mock_ook):
        self.assertIsNone(ook())

Assuming you’ve saved that file as quicktest.py, the unit tests give this result:

$ python -m unittest quicktest
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

And of course, from a.b import c gives you a plain variable c in your package, so this same mechanism should work.

Answered By: Dan Passaro

I used Dan Passaro’s solution till I came across this one using patch.object – which looks even better to me:

from unittest.mock import patch,
from .. import monkey
[...]
@patch.object(monkey, 'ook', Mock(return_value=None))
def test_run_ook (self, mock_ook):
    self.assertIsNone(monkey.ook())
    mock_ook.run.assert_called_once_with('')

Advantages:

  • No need for the boilerplate code that is __name__ + '.object_to_be_mocked'
  • All dependencies of the test case are clearly stated at the beginning of the file as import statements.
  • In cases where the dotted name of the object you’re trying to mock out is longer (say "amazon.jungle.monkey.ook") and you therefore write @patch.object(amazon.jungle.monkey, 'ook', …), your IDE’s static code analysis can make sure that at least amazon.jungle.monkey is a valid variable since you didn’t write the whole thing as a string 'amazon.jungle.monkey.ook'.

Disadvantages:

  • You cannot do from ..monkey import ook but need to do from .. import monkey and access ook through monkey, i.e. monkey.ook. In cases where I need to write this often I will add ook = monkey.ook to the beginning of my tests for convenience. (Or even to the import statements in case I never need to mock out this particular property of monkey.)
Answered By: balu

When you do from ..monkey import ook from a module pkg1.pgk2.mymodule what you end up is with pkg1.pgk2.mymodule.ook.

That is now ook is in the namespace of the module where you performed the from ... import .... And that is the target that you need to patch.

So you just patch pkg1.pkg2.mymodule.ook:

from unittest.mock import patch # mypackage.mymodule.patch
from ..monkey import ook        # mypacket.mymodule.ook

with patch("pkg1.pgk2.mymodule.ook"):
   ....

As pointed out by others, if you are patching from the same module where you did the import then you can use __name__ to get the dotted package name, but if you are patching from another module you need to spell it out.

Just remember that whatever you import is patcheable from target modulethatimports.nameimported.

Answered By: RubenLaguna

Building on the accepted answer, I believe that this is the cleanest way to achieve desired goal:

from mock import patch
from .. import monkey

@patch(monkey.__name__+'.ook', Mock(return_value=None))
def test_run_ook (self, mock_ook):
    self.assertIsNone(monkey.ook())
    mock_ook.run.assert_called_once_with('')
Answered By: moomima
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.