Patch#

You can change the behavior of an existing function or method by patching it with unittest.mock.patch. You just need to specify the target, which refers to an object in Python. Find out more in the specific documentation page.

import requests

import unittest
from unittest.mock import patch

from sklearn.neighbors import KNeighborsRegressor

Target#

There’s always some mystery associated with specifying the target for mocking; this section focuses on the details.

Target should be a string in the form ‘package.module.ClassName’. The target is imported and the specified object replaced with the new object, so the target must be importable from the environment you are calling patch() from. The target is imported when the decorated function is executed, not at decoration time.

Note: All targets in these notebook examples begin with the __main__ section. This allows the patched object to be referenced in the same module as the patching code.


The following example shows really typical case. For testing code that handles API reponse, but it’s typical not ot have access to the API during code development - mocking output of the requesting tool is good option in such case.

def request_user(user_id):
    response = requests.get(f"https://im_not_exist/{user_id}")
    if response.ok:
        return response.text
    else:
        return "Fail!"

Patching requests.get to return an object with the attribute ok=True allows us to ensure that we receive a response in a successful case.

with patch("__main__.requests.get") as mocked_get:    
    mocked_get.return_value.ok = True
    mocked_get.return_value.text = "Success"
    print(request_user("User"))
Success

Conversely, simulating an error response case returns the message corresponding to a failure.

with patch("__main__.requests.get") as mocked_get:
    mocked_get.return_value.ok = False
    mocked_get.return_value.text = "Success"
    print(request_user("User"))
Fail!

Class method#

You can modify the behavior of a class method as well, simply by specifying the path to it after the class name.


As an example, consider another common case in my practice. When writing code that needs to handle a machine learning model, you don’t necessarily need to use a specific model. Mocking the model’s results is a good way to verify if everything works correctly. The following cell shows how to define the result of the predict method of the __main__.KNeighboursRegressor class.

with patch("__main__.KNeighborsRegressor.predict") as predict:
    regressor = KNeighborsRegressor()
    predict.return_value = 'predict out'
    print(regressor.predict())
predict out

Dict#

With patch.dict you can redefine old and define new values of the dictionary.

Note: It can also handle dict like objects not only dicts.

Note: By default it leaves all unmentioned values unchanged - with the clear=True argument you can force python to clear all unmentioned keys.


The following cell creates my_dict, which we’ll use for experiments, and shows how its content changes under the patch.dict context manager and outside of it.

my_dict = dict(val=10, val2=3)
with patch.dict("__main__.my_dict", {"hello": 10, "val": 3}):
    print(my_dict)
print(my_dict)
{'val': 3, 'val2': 3, 'hello': 10}
{'val': 10, 'val2': 3}

Note: that val2, which isn’t mentioned when invoking patch.dict, stores its values in the context manager. The following code does the same, but uses the clear=True argument, which results in the absence of this field in the result.

with patch.dict("__main__.my_dict", {"hello": 10, "val": 3}, clear=True):
    print(my_dict)
{'hello': 10, 'val': 3}

The following code shows a very typical case related to mocking specific values for environment variables - patching os.environ, which is a dict-like object.

import os
with patch.dict("os.environ", {"MY_VALUE": "13"}):
    print(os.environ["MY_VALUE"])
13

Syntax#

There are two general ways to define a patch: through the with context manager or the @ decorator.


The following example demonstrates patching using a context manager. A function is defined, and we have details about the function call within the context manager block.

def my_function(a, b):
    return a + b

with patch("__main__.my_function") as p:
    my_function("hello")
    print(p.call_args)
call('hello')

Method decoration#

Decoration offers more flexibility in its usage. The function being decorated must include a special argument for the mock object, through which you can control the behavior of the target.


The following example demonstrates a test case where test_method is decorated so that any call to hello will be modified within it.

def hello(): return "hello"


class SomeTest(unittest.TestCase):
    
    @patch("__main__.hello", return_value="bye bye")
    def test_method(self, mocked_hello):
        print(hello())


ans = unittest.main(argv=[''], verbosity=0, exit=False)
del SomeTest
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
bye bye

Class decoration#

Instead of decorating just one method in a test case, you can decorate the entire test case by decorating the whole class.


The following cell shows applying patch as a decorator on the entire test case, demonstrating that all methods of the class exhibit modified behavior in this case.

def hello(): return "hello"

@patch("__main__.hello", return_value="bye bye")
class SomeTest(unittest.TestCase):
    def test_method1(self, mock):
        print("method1", hello())

    def test_method2(self, mock):
        print("method2", hello())

ans = unittest.main(argv=[''], verbosity=0, exit=False)
del SomeTest
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK
method1 bye bye
method2 bye bye

Several decorators#

You can patch multiple objects by specifying multiple decorators. For each patch, you must provide a mock argument. The rules for determining which parameter belongs to which patch are as follows:

  • The parameter corresponding to the method patch comes before the parameter corresponding to the class patch.

  • It might feel counterintuitive that parameters for decorators specified earlier come later than those for more nested parameters—but this makes sense if you consider how decorators work, as each subsequent decorator wraps the previous one.


The following example demonstrates how this works. There are three decorators: one applied to the entire class and two applied to specific methods. In each method, arguments are called in a way that allows us to identify the type of decoration. The output shows which argument corresponds to each decorator.

import unittest.mock

def fun1(): pass
def fun2(): pass
def fun3(): pass

@patch("__main__.fun1")
class SomeTest(unittest.TestCase):
    @patch("__main__.fun3")
    @patch("__main__.fun2")
    def test_value(
        self, 
        arg1: unittest.mock.MagicMock, 
        arg2: unittest.mock.MagicMock, 
        arg3: unittest.mock.MagicMock
    ):
        fun1("Patched by class")
        fun2("Inner method patch")
        fun3("Outer method patch")
        
        print(arg1.call_args)
        print(arg2.call_args)
        print(arg3.call_args)

ans = unittest.main(argv=[''], verbosity=0, exit=False)
del SomeTest
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK
call('Inner method patch')
call('Outer method patch')
call('Patched by class')

Combining#

The question is: if you combine different options for defining decoration, which modification will take precedence? It appears the priority, in decreasing order, is as follows: context manager, class decorator, method decorator.


The following cell shows a test case decorated to modify the hello function. However, each method within the class modifies the same function in different ways:

def hello(): return "hello"

@patch("__main__.hello", return_value="class decoration patching")
class SomeTest(unittest.TestCase):
    def test_method1(self, mock):
        with patch("__main__.hello", return_value="manager patching"):
            print("method1", hello())

    @patch("__main__.hello", return_value="method patching")
    def test_method2(self, mock, mock2):
        print("method2", hello())

ans = unittest.main(argv=[''], verbosity=0, exit=False)
del SomeTest
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK
method1 manager patching
method2 class decoration patching

As a result, we see messages showing the output from the context manager taking priority over the class decoration messages, and the class decoration messages taking priority over the method decoration messages.

Patch handler#

Mocking with any syntax produces the object that should be the handler for the behavior of the mocked object - this object should be of type MagicMock.


The following cell implements the function that supposed to be patched, and patches it in two different syntaxes. The code under the patch just prints the type of the object created by the patch.

def test_func(): pass

# decorator syntax
@patch("__main__.test_func")
def something(ans):
    print(type(ans))
something()

# context manager syntax
with patch("__main__.test_func") as ans:
    print(type(ans))
<class 'unittest.mock.MagicMock'>
<class 'unittest.mock.MagicMock'>

Patch object#

The patch.object allows you to specify the targed defined in the current environment as the target for the mock.


The following cell defines the class, that we’ll use as an example.

class MyExample:
    def example_method(self):
        print("hello")

To mock it’s example_method with just patch you have to specify target as a string, in this case it would be __main__.MyExample.example_method.

The patch.object allows direct access to the MyExample. The following cell shows an example of retrieving arguments passed during the call to the mocked method.

with patch.object(target=MyExample, attribute="example_method") as mock:
    new = MyExample()
    new.example_method(10, 20, 30)
    ans = mock.call_args

ans
call(10, 20, 30)

Patch in module#

A really useful practical application is that the module in python is an objece as well, so you can select any attribute of the module and mock it using the patch.object approach.


The following cell creates the module that we’ll use as an example. It contains mocked_function, which does nothing and should to be mocked and tested_function, which returns the output of the tested_function - so all this will work if mocked_funciton really replaced mocked_function.

%%writefile /tmp/module_for_testing.py
def mocked_function(): pass

def tested_function():
    return mocked_function()
Overwriting /tmp/module_for_testing.py

The main features of the following cell is how it uses patch.object: target takes module attribute is a name of the function to be patched.

import sys
sys.path.append("/tmp")
import module_for_testing
from module_for_testing import tested_function

with patch.object(
    target=module_for_testing,
    attribute="mocked_function"
) as mock:
    mock.return_value = "I was mocked"
    print(tested_function())
I was mocked

Finnaly in the patch context, tested_function() returns the value specified in the mock.return_value.

Create#

In case exact mocked attribute doesn’t exist generally you will get corresponding error. But if you need to test case where attribute must be, you can use create=True parameter - in such case attribute under consideration will be created as a mock object.


The following cell creates a class that has no attributes in it - we’ll use it for mocking attributes.

class MyExample:
    pass

The following cell shows an attempt to mock the hello attribute of the MyExample.

try:
    with patch.object(target=MyExample, attribute="hello"): pass
except Exception as e:
    print(e)
<class '__main__.MyExample'> does not have the attribute 'hello'

Since hello is not defined in the MyExample class, it returns an error. In contrast, the following cell shows patch.object with the parameter create=True.

with patch.object(target=MyExample, attribute="example_method", create=True): pass

At first it may seem like a useless feature, but the following cell shows case when you may need to use it - suppose we need to test the logic that depends on the presence of the named attribute. The following cell shows a test that triggers a behavior that depends on the presence of the hello attribute.

class MyExample:
    def my_test(self):
        if hasattr(self, "hello"):
            return 1
        return 0


with patch.object(target=MyExample, attribute="hello", create=True) as mock:
    print(MyExample().my_test())
1

New#

patch and patch.object can set a specific value for the mocked object - using the new parameter. It’s useful to test cases where some attributes of the mocked object take specific values.

Note The argument passed to the funciton by the decorator and the tartet of the with statement now will be the value specified in new, not the mock object.


The following cell creates a MyExample class with a value attribute that is supposed to mock.

class MyExample:
    value = 10

The following cell defines the context for the patch.object that uses new. The context prints the corresponding attribute of the MyExample() and the target of the context.

with patch.object(
    target=MyExample,
    attribute="value",
    new="hello from mock"
) as value:
    print("value:", MyExample().value)
    print("handler:", value)
value: hello from mock
handler: hello from mock

Both take the value specified by the new parameter.