Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock using patch relative paths?

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?

like image 299
Sardathrion - against SE abuse Avatar asked Jan 15 '13 16:01

Sardathrion - against SE abuse


People also ask

What is Patch in mock?

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.

What is the difference between mock and patch?

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.

What is mock MagicMock?

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'

What is spec in MagicMock?

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).


2 Answers

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.)
like image 74
balu Avatar answered Oct 31 '22 00:10

balu


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.

like image 27
Dan Passaro Avatar answered Oct 31 '22 02:10

Dan Passaro