How To Use Patch In Python Unittest Python

Mar 19th, 2021 - written by Kimserey with .

When writing unit tests, we sometime must mock functionalities in our system. In Python unittest.mock provides a patch functionality to patch modules and classes attributes. In this post, we will look at example of how to use patch to test our system in specific scenarios.

Patch Classes

Patch can be used as a decorator or a context manager. In this post we’ll use it as a context manager which will apply the patch within a with block.

We start by creating a dumb class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# in my_class.py

class MyClass:
    my_attribute = 1

    def __init__(self):
        self.value = 2

    def do_something(self):
        return "hello {}".format(self.value)

def get_value():
    o = MyClass()
    return o.value

We created a module my_class with a class MyClass with:

  • an instance attribute value,
  • a class attribute my_attribute,
  • a method do_something.

We also added a method get_value which returns the instance attribute value.

In the following steps we will demonstrate how to patch the instance attribute, the class attribute and instance attribute of MyClass.

Patch Instance Attribute

Starting from the instance attribute being used in get_value, we can patch it by patching my_class.MyClass.

1
2
3
4
5
6
from unittest.mock import patch
from my_class import MyClass, get_value

with patch("my_class.MyClass") as mock:
    mock.return_value.value = "hello"
    print(get_value())

The result of patch is a MagicMock which we can use to set the value attribute.

1
mock.return_value.value = "hello"

return_value would be the instance itself (from MyClass()) where we mock on it value.

The result of print(get_value()) will then be Hello rather than 2.

Patch Class Atribute

For the class attribute, we can use patch.object which makes it easier as we can direclty pass the reference of the class.

1
2
3
4
5
6
7
from unittest.mock import patch
from my_class import MyClass

with patch.object(MyClass, "my_attribute", "hello"):
    o = MyClass()
    print(MyClass.my_attribute)
    print(o.my_attribute)

The third argument of patch.object is the value of the attribute to be patched.

Patch Method

Similarly we can use patch.object to patch class method.

1
2
3
with patch.object(MyClass, "do_something", return_value="hello"):
    o = MyClass()
    print(o.do_something())

We use the two arguments signature where we specify return_value. The difference with the three arguments signature is that using return_value patches a method rather than a class attribute.

While patching methods, we can also access the call arguments using call_args from the patch result.

Patch Chained Methods

Another common scenario is when we have chained methods for example such a call MyClass().get_client().send():

1
2
3
4
5
6
7
8
9
10
11
12
class Client:
    def send(self):
        return "Sent from client"

class MyClass:
    my_attribute = 20

    def get_client(self):
        return Client()

def send():
    return MyClass().get_client().send()

From what we’ve learnt we can easily patch Client.send using patch.object:

1
2
with patch.object(Client, "send", return_value="hello"):
    print(send())

but we can also patch MyClass.get_client and mock the whole chain:

1
2
3
with patch.object(MyClass, "get_client") as mock:
    mock.return_value.send.return_value = "hello"
    print(send())

We start by mocking method get_client, then mock the return_value.send which returns a mock of the send method which we then mock the return_value resulting in Client.send being mocked.

Patch Module Functions

Lastly we’ll see how we can mock a module function. In this example we have a second module lib which contains a function some_function:

1
2
3
# in lib.py
def some_function():
    return "Test"

We import that function from my_class which we call in test:

1
2
3
4
5
# in my_class.py
from lib import some_function

def test():
    return some_function()

If we want to patch some_function, we can do so with patch:

1
2
with patch("my_class.some_function", return_value="hello"):
    print(test())

One important point to note is that we have to patch from my_class.some_function rather than lib.some_function. This is because some_function is imported in my_class hence this is the instance that needs to be mocked.

If we need to use arguments to construct the return value, we can also use a lambda:

1
with patch("my_class.some_function", lambda x: "hello {}".format(x)):

And that concludes today’s post!

Conclusion

In today’s post, we looked at unittest.mock patch functionality. We started by looking at how we could patch a class attribute, an instance attribute and a method. And we completed the post by looking at how we could patch a module. I hope you liked this post and I see you on the next one!

External Sources

Designed, built and maintained by Kimserey Lam.