Looking at this webpage: http://www.toptal.com/python/an-introduction-to-mocking-in-python -- The author talks about Mocking and Patching in Python and gives a pretty solid "real-world" example. The part that is tripping me up is understanding how the unit testing frame work knows which mock object gets passed to which patch.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import os.path
def rm(filename):
if os.path.isfile(filename):
os.remove(filename)
Code sample is pretty easy to understand. Hard-coded dependency on the OS library/module. First checks if the file exists using the os.path.isfile()
method and if so, removes it using os.remove()
Test/Mock code is as follows:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from mymodule import rm
import mock
import unittest
class RmTestCase(unittest.TestCase):
@mock.patch('mymodule.os.path')
@mock.patch('mymodule.os')
def test_rm(self, mock_os, mock_path):
# set up the mock
mock_path.isfile.return_value = False
rm("any path")
# test that the remove call was NOT called.
self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")
# make the file 'exist'
mock_path.isfile.return_value = True
rm("any path")
mock_os.remove.assert_called_with("any path")
I guess what's confusing me is that there are 2 @Patch calls and 2 parameters passed in the test. How does the unit testing framework know that mymodule.os.path
is patching os.path
and that it is mapped to mock_path
? And where is mymodule.os.path
defined?
(There appears to be a lot of "magic" going on and I'm not following it.)
A object that you want to test may have dependencies on other complex objects. To isolate the behavior of the object you want to test you replace the other objects by mocks that simulate the behavior of the real objects. So in simple words, mocking is creating objects that simulate the behavior of real objects.
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.
Patch can be used as a TestCase class decorator. It works by decorating each test method in the class. This reduces the boilerplate code when your test methods share a common patchings set. patch finds tests by looking for method names that start with patch.
MagicMock objects provide a simple mocking interface that allows you to set the return value or other behavior of the function or object creation call that you patched. This allows you to fully define the behavior of the call and avoid creating real objects, which can be onerous.
it goes by the order of the execution of the decorators and that is also the order of the parameters passed on to your test method...
order of decorators execution is shown here: https://thadeusb.com/weblog/2010/08/23/python_multiple_decorators/
When you use patch the way you wrote it, a Mock instance it is automatically created for you and passed as a parameter to your test method. there is another version of it:
@mock.patch("subprocess.check_output", mock.MagicMock(return_value='True'))
def test_mockCheckOutput(self):
self.assertTrue(subprocess.check_output(args=[])=='True')
in this case you pass your own Mock object and in this example, when you call subprocess.check_output(), it will return 'True'
you could however do:
def test_mockCheckOutput(self):
m = mock.MagicMock(return_value='True')
with mock.patch("subprocess.check_output", m):
self.assertTrue(subprocess.check_output(args=[])=='True')
and in this case you can pass any mock item you want because it will be evaluated during runtime... :)
When applying a decorator, it is good to look at it like this
<wrapper1>
<wrapper2>
<wrapper3>
**Your Function**
</wrapper3>
</wrapper2>
</wrapper1>
Basically your function is required to interact with the wrappers in this order:
wrapper3
-> wrapper2
->wrapper1
@wrapper1
@wrapper2
@wrapper3
def your_func(wrapper1.input, wrapper2.input, wrapper3.input):
NOTE wrapper1.input isn't how you would actually reference its input
To answer the second part of your question, how mymodule.os
knows to refer to os. When Patching you are effectively intercepting calls to that specific name. When you call os
in mymodule
you are effectively calling mymodule.os
. When patching you must refer to the class that is being mocked by the way it is being called in the actual code, not from the test modules perspective
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