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 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:
value
,my_attribute
,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
.
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
.
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.
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.
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.
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!
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!