Mocking/Patching Class & Instance variables in Python Testing
This article provides an overview of how to use the unittest module to mock or patch class and instance variables within a Python class. By the end of this article, you will have gained the following knowledge:
- How to patch/mock class attributes.
- How to patch/mock instance attributes.
- The distinction between
patchandpatch.objectin theunittestmodule.
The unittest module in Python is an integrated testing framework that offers a range of tools for writing and executing test cases. It is an integral part of the Python standard library and is utilized to perform unit testing, a critical practice in software development that ensures the accuracy of individual components or functions within a program.
Each test case class typically incorporates methods that examine specific aspects of your code. These methods are conventionally named with “test” as the prefix and include assertions to validate anticipated behaviors.
Using patch():
1. Mocking/Patching a Class attribute!
Class attributes are variables or methods that belong to the class and are shared among all instances of the class.
Now, let’s begin by mocking a class variable. Below is a simple class named “Car” in the file “variables.py,” which includes “cost” as a class variable or attribute.
#variables.py file
class Car:
cost = 2000
print(Car.cost) # prints 2000When we print Car.cost, it displays the expected value of 2000. However, I intend to modify this behavior; my goal is to display 5000 when the cost value is printed. To accomplish this, we can utilize the patch function from the unittest.mock module.
The patch function serves the purpose of modifying objects for testing purposes. It proves useful for mocking attributes of classes, instances, and even module attributes. Its versatility makes it capable of addressing a variety of scenarios where modification is required.
To illustrate this functionality, we will create a TestCar class dedicated to testing these modifications in a separate file named test_variables.py.
# test_variables.py
from unittest.mock import patch # import patch from unittest.mock file
from variables import Car # importing Car class from variables file
class TestCar:
def test_value_with_patch(self):
# mocking a class variable
# provide `file_name.class_name.variable_name` as str
with patch('variables.Car.cost', 5000):
print("\n", Car.cost) # this prints 5000We are employing a context manager (using the with statement) that utilizes the patch function to temporarily alter the class variable cost.
The patch function requires a fully qualified name for the targeted class variable we intend to modify, along with the new value that is temporarily assigned to the class variable. In this particular instance, we are providing variables.Car.cost as the path to the class variable cost, and the value 5000 is the one we temporarily assign to cost.
In the case of variables.Car.cost, the term variables corresponds to the file name, Car stands for the class name, and cost represents the class variable we are temporarily adjusting to return the value 5000.
2. Mocking/Patching a Class Method!
Now that we have learned how to patch a class variable, let’s proceed to understand how to patch a class method.
We will accomplish this by writing a class method called get_cost() that returns the value of the class variable cost.
# variables.py
class Car:
cost = 2000
@classmethod
def get_cost(cls):
return cls.cost
print(Car.cost) # prints 2000
print(Car.get_cost()) # prints 2000The line print(Car.get_cost()) currently outputs 2000. I intend to modify the behavior of the get_cost() class method by mocking it to return a value of 10000.
To achieve this, I will be creating a new test case named test_get_cost() within the TestCar class. This test case will be designed to assess the functionality of the get_cost() class method.
# test_variables.py
from unittest.mock import patch
from variables import Car # importing Car class from variables file
class TestCar:
def test_value_with_patch(self):
# mocking a class variable
# provide `file_name.class_name.attribute_name` as str
with patch('variables.Car.cost', 5000):
print("\n", Car.cost)
def test_get_cost(self):
# mocking a class method
# provide `file_name.class_name.attribute_name` as str
# return_value tells the what to return for the get_cost()
with patch("variables.Car.get_cost", return_value=10000):
print("\n", Car.get_cost()) # this prints 10000
# still prints 2000, because we are patching get_cost not cost
print(Car.cost) # prints 2000 stillTake note of the line with patch("variables.Car.get_cost", return_value=10000). In this case, we are using the patch method to simulate the behavior of get_cost(). To achieve this, we need to furnish the complete path to the class method.
variables.Car.get_cost: In this string, "variables" pertains to the file name, "Car" refers to the class name, and "get_cost" signifies the class method that we intend to substitute with a return value of 10000.
The return_value parameter designates the specific value that the mock function should provide as output.
Please remember we are patching the
get_cost()method here. Not directly thecostvariable. So,print(Car.cost)still prints 2000.
Using patch.object():
If the value of a variable varies from object to object, then such variables are called instance variables. For every object, a separate copy of the instance variable will be created.
Instance variables are not shared by objects. Instance variable is a variable which belongs to the object/instance of the class. It gets created when an instance is created and destroyed when the instance is destroyed.
1. Mocking an instance variable!
Let us create a new instance variable insurance with value 5000. Modify the class Carto include the instance variable insurance.
# variables.py
class Car:
cost = 2000
def __init__(self):
# creating an instance variable
self.insurance = 5000
@classmethod
def get_cost(cls):
return cls.cost
print(Car.cost) # prints 2000
print(Car.get_cost()) # prints 2000
# create an instance for the class Car
car = Car()
print(car.insurance) # prints 5000I intend to patch the insurance variable, which is one of our instance variables, in order to make it return a value of 1000. To achieve this, the unittest.mock library provides the patch.object() function specifically designed for patching instance attributes.
Get Hemachandra’s stories in your inbox
Join Medium for free to get updates from this writer.
Now, let’s proceed to create the test method for mocking the instance variable. We will need to make modifications to the test_variables.py file as outlined below:
# test_variables.py
from unittest.mock import patch
from variables import Car # importing Car class from variables file
class TestCar:
def test_value_with_patch(self):
# mocking a class variable
# provide `file_name.class_name.attribute_name` as str
with patch('variables.Car.cost', 5000):
print("\n", Car.cost)
def test_get_cost(self):
# mocking a class method
# provide `file_name.class_name.attribute_name` as str
with patch("variables.Car.get_cost", return_value=10000):
print("\n", Car.get_cost()) # this prints 10000
# still prints 2000, because we are patching get_cost not cost
print(Car.cost) # prints 2000 still
def test_insurance_variable(self):
# mocking an instance variable
car1 = Car()
# "insurance" is present in the car1 instance, so provide the name
# remember that we have to provide the name of variable in str
with patch.object(car1, "insurance", new=1000):
print(car1.insurance) # prints 1000 Observe the line with patch.object(car1, "insurance", new=1000)
In this context:
car1is an instance of a class, likely an instance of theCarclass in your code."insurance"is the attribute of thecar1instance that you want to temporarily modify.new=1000specifies that you want to set the value of the"insurance"attribute to1000for the duration of the context managed block.
Inside the context managed block, any code that accesses the insurance attribute of the car1 instance will see its value as 1000 instead of its original value.
2. Mocking/Patching an Instance method!
Now that we have learned how to patch an instance variable, let’s proceed to understand how to patch an instance method — essentially, a method of an object.
We will achieve this by adding an instance method to the Car class in the variables.py file, as demonstrated below:
# variables.py
class Car:
cost = 2000
def __init__(self):
# creating an instance variable
self.insurance = 5000
@classmethod
def get_cost(cls):
return cls.cost
def get_insurance(self):
return self.insurance
print(Car.cost) # prints 2000
print(Car.get_cost()) # prints 2000
# create an instance for the class Car
car = Car()
print(car.insurance) # prints 5000
print(car.get_insurance()) # prints 5000I want to mock the get_insurance() instance method to return 3000 as the new value.
Please remember that I am only mocking the
get_insurance()method but not the value of theinsurancevariable.
Let us write a new test case for testing this get_insurance() instance method in the TestCar class of test_variables.py file.
# test_variables.py
from unittest.mock import patch
from variables import Car # importing Car class from variables file
class TestCar:
def test_value_with_patch(self):
# mocking a class variable
# provide `file_name.class_name.attribute_name` as str
with patch('variables.Car.cost', 5000):
print("\n", Car.cost)
def test_get_cost(self):
# mocking a class method
# provide `file_name.class_name.attribute_name` as str
with patch("variables.Car.get_cost", return_value=10000):
print("\n", Car.get_cost()) # this prints 10000
# still prints 2000, because we are patching get_cost not cost
print(Car.cost) # prints 2000 still
def test_insurance_variable(self):
# mocking an instance variable
car1 = Car()
# "insurance" is present in the car1 instance, so provide the name
# remember that we have to provide the name of variable in str
with patch.object(car1, "insurance", new=1000):
print(car1.insurance) # prints 1000
def test_insurance_method(self):
# mocking an instance method
car = Car()
# "get_insurance" is present in the car instance, so provide the name
# remember that we have to provide the name of the method in str
with patch.object(car, "get_insurance", return_value=3000):
print(car.get_insurance()) # prints 3000Let us breakdown the test_insurance_method() code.
The patch.object context manager from the unittest.mock module is to temporarily mock the get_insurance() method of an instance of the Car class.
patch.object(car, "get_insurance", return_value=3000): This line sets up a context in which theget_insurance()method of thecarobject will be temporarily replaced with a mock that returns3000as its result. Thereturn_valueparameter specifies the value that the mock should return when the method is called.print(car.get_insurance()): Inside the context, when theget_insurance()method of thecarobject is called, it will return the mocked value of3000, rather than executing the actual method logic. This allows you to isolate and test specific parts of your code without affecting the real behavior of the method.
The purpose of this code is to test the behavior of code that interacts with the get_insurance() method of the car object. By temporarily replacing the method with a mock that returns a known value (3000 in this case), you can ensure that your test results are predictable and consistent, regardless of the actual implementation of the get_insurance() method.
Once the code inside the
patch.objectblock is executed, the original behavior of theget_insurance()method is restored, and subsequent calls tocar.get_insurance()will behave as usual.
By this time, you should have understood the basic difference between patch and patch.object. Let us summarize again
patchis more high level function to patch/mock attributes of class.patch.objectis useful in patching/mocking instance attributes of an instance of a class.- Patching an instance attribute of an instance belongs to that particular instance and doesn’t get applied to another instance because every instance of a class gets a new copy of the instance attributes.
The methods I employed for both patch and patch.object are not exhaustive; there exist several other ways to utilize them. These functions can also serve as decorators. For further information, please consult the documentation provided in the unittest module.
Until then, your friend here, Hemachandra, is signing off…
For more courses, you can visit my personal website.
Have a great day!









