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?
patch() unittest. mock provides a powerful mechanism for mocking objects, called patch() , which looks up an object in a given module and replaces that object with a Mock . Usually, you use patch() as a decorator or a context manager to provide a scope in which you will mock the target object.
Mock is a type, and patch is a context. So you are going to pass or receive Mock instances as parameters, and apply patch contexts to blocks of code. (Lowercase 'mock' is just the name of the package.) Tests and test classes are often decorated with calls to patch.
The MagicMock class is just a Mock variant that has all of the magic methods pre-created for you (well, all the useful ones anyway). The following is an example of using magic methods with the ordinary Mock class: >>> >>> mock = Mock() >>> mock. __str__ = Mock(return_value='wheeeeee') >>> str(mock) 'wheeeeee'
mock documentation: spec: This can be either a list of strings or an existing object (a class or instance) that acts as the specification for the mock object. If you pass in an object then a list of strings is formed by calling dir on the object (excluding unsupported magic attributes and methods).
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:
__name__ + '.object_to_be_mocked'
import
statements.@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:
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
.)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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With