This blog comprises 3 parts:
- Part 1 – Fundamentals of Integration Testing with Django Rest Framework
- Part 2 – Django Token-Based Authentication Test Guidelines
- Part 3 – How to Mock Methods in Django Tests (This Blog)
We hope you find this blog useful. In this blog series, we will explore integration testing with the Django Rest Framework in detail, as the goal is to enhance your understanding of testing in Django and help you build robust applications.
Part 2 – How to Mock Methods in Django Tests
When writing test cases for Django applications, there are situations where you need to mock certain methods to control their behaviour during testing. Mocking allows you to simulate the behaviour of methods without actually executing their actions. This is particularly useful when you want to isolate specific functionalities for testing and prevent unintended consequences, such as sending real emails or making external API requests. In this blog post, we’ll explore how to mock methods and external requests in Django tests.
Example
For mocking, we need to import mock. It can be imported, as mentioned below in the example.
from unittest import mock
Let us consider a scenario where we have a class Utils, and inside that class, there is a method with the name send_email, as shown in the example below.
class Utils:
@classmethod
def send_email(cls, username):
//method body//
In our view, this method is called as shown in the example below.
Views
class EmployeeCollectionView(generics.CreateAPIView):
name = 'employee-collection-view'
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer
def post(self, request, *args, **kwargs):
username = self.request.auth_context['user']
# This sends an actual email to the user
Utils.send_email(username)
return self.create(request, *args, **kwargs)
In situations where an email is sent along with adding an employee to the employee table, it may not be desirable to send an email every time the test suite is run. To address this, it is common practice to mock these methods to prevent their execution during testing. The purpose of mocking is to simulate the behaviour of the original methods without actually performing their actions. The mock methods can be configured to either return nothing or mimic the return values of the actual methods.
In order to mock the method in our test cases, we can mock it as shown in the example below.
def test_post(self):
payload = {
'name': 'Andy',
'designation': 'Manager'
}
request = self.factory.post(BASE_URL, data=payload)
# This line mocks the method. So every time we run the test suite, the
# method won’t be executed and will return None. We can also use it to
# to mimic the return behavior of the function if the return value is used
# further
Utils.send_email = mock.Mock(return_value=None)
response = EmployeeCollectionView.as_view()(request)
self.assertEqual(response.status_code, 201)
In the case where the method is returning some value, we can mock it and return the desired value from the method, as shown in the example below.
mock_response = {
'name': 'Andy',
'type': 'User',
}
class_name.method_name = mock.Mock(return_value=mock_response)
And we are done! This is how we can mock methods.
How to Mock the Requests Library for Reliable Testing
When writing test cases for your code that relies on external data sources, it’s essential to have control over the responses you receive. This control ensures that your tests remain stable and predictable, even if the external data source changes over time. One effective way to achieve this control is by mocking the responses from the request library.
What are Mock Requests?
Mock requests allow you to simulate the interaction between your code and an external data source. By doing so, you gain complete control over this interaction, enabling you to create stable and reliable test cases. In this blog post, we’ll explore how to mock responses from the request library with a practical example.
Example
Let us consider that we have an app with the name employee, and inside the app, we have a file utils.py. Inside the utils.py file, we have a class called ServiceCall.In ServiceCall, we have a method that makes a get request to the external API, as shown in the example below.
employee/utils.py
class ServiceCall:
@classmethod
def get(url):
requests.get(url=url, headers=headers)
return response
In our view, we are using the above method to get the response from the external API, as shown below in the example
Views
class EmployeeDetailsView(generics.RetrieveAPIView):
name = 'employee-details-view'
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer
def get(self, request, *args, **kwargs):
url = 'www.example.com'
response = ServiceCall.get(url)
return Response(response.json())
When writing the test cases for the above view, we can mock the response of the above view, as shown in the below example.
Importing mock
from unittest import mock
from unittest.mock import Mock
Mocking response in Test Cases
In the above view, we’re returning the mocked response we’re getting from the ServiceCall.get method, which returns a response object. We add .json() method to get the json of the response. The mocking we learned above won’t work as it would need a .json() method to be implemented.
You can mock such responses from the requests library as below:
def test_post(self):
request = self.factory.get(BASE_URL)
# Create a new requests.Response() object
resp = requests.models.Response()
# Assign the desired response as {“key”: “value”}
resp._content = str.encode(json.dumps({“key”: “value”}))
# Assign status code
resp.status_code = 200
# Return resp (requests.Response)
ServiceCall.get = mock.Mock(return_value=resp)
response = EmployeeCollectionView.as_view()(request)
self.assertEqual(response.status_code, 200)
And we are done !! This is how we mock requests library responses.
Conclusion
Mocking methods and external requests is a crucial aspect of writing comprehensive and reliable test cases for Django applications. By using the unittest.mock library, you can gain full control over the behaviour of methods and simulate interactions with external services. This ensures that your tests remain predictable and unaffected by changes in external data sources or unintended side effects.
With the techniques discussed in this blog post, you can confidently write test cases that cover various scenarios in your Django projects while maintaining control and predictability in your tests.
Mocking responses from the request library is a powerful technique for ensuring predictable and reliable test cases. By following the example provided in this blog post, you can effectively control the behaviour of external data sources in your tests, helping you build robust and maintainable code.
Author
Nikhil Chopdekar
Asso. Lead Engineer