How To Mock Using Patch Relative Paths?
Solution 1:
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))deftest_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 leastamazon.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 dofrom .. import monkey
and accessook
throughmonkey
, i.e.monkey.ook
. In cases where I need to write this often I will addook = 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 ofmonkey
.)
Solution 2:
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"classOokTest(unittest.TestCase):
deftest_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)deftest_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.
Solution 3:
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))deftest_run_ook (self, mock_ook):
self.assertIsNone(monkey.ook())
mock_ook.run.assert_called_once_with('')
Solution 4:
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))deftest_run_ook (self, mock_ook):
self.assertIsNone(ook())
mock_ook.run.assert_called_once_with('')
Solution 5:
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.patchfrom ..monkey import ook # mypacket.mymodule.ookwith 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
.
Post a Comment for "How To Mock Using Patch Relative Paths?"