8
votes

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

2

2 Answers

6
votes

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

2
votes

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