Writing unit tests for a RESTful Web Service

Now, we will write our first round of unit tests related to the drone category class based views: DroneCategoryList and DroneCategoryDetail. Open the existing restful01/drones/tests.py file and replace the existing code with the following lines that declare many import statements and the DroneCategoryTests class. The code file for the sample is included in the hillar_django_restful_10_01 folder in the restful01/drones/tests.py file:

from django.utils.http import urlencode 
from django.core.urlresolvers import reverse 
from rest_framework import status 
from rest_framework.test import APITestCase 
from drones.models import DroneCategory 
from drones import views 
 
 
class DroneCategoryTests(APITestCase): 
    def post_drone_category(self, name): 
        url = reverse(views.DroneCategoryList.name) 
        data = {'name': name} 
        response = self.client.post(url, data, format='json') 
        return response 
 
    def test_post_and_get_drone_category(self): 
        """ 
        Ensure we can create a new DroneCategory and then retrieve it 
        """ 
        new_drone_category_name = 'Hexacopter' 
        response = self.post_drone_category(new_drone_category_name) 
        print("PK {0}".format(DroneCategory.objects.get().pk)) 
        assert response.status_code == status.HTTP_201_CREATED 
        assert DroneCategory.objects.count() == 1 
        assert DroneCategory.objects.get().name == new_drone_category_name 

The DroneCategoryTests class is a subclass of the rest_framework.test.APITestCase superclass and declares the post_drone_category method that receives the desired name for the new drone category as an argument.

This method builds the URL and the data dictionary to compose and send an HTTP POST request to the view associated with the views.DroneCategoryList.name name (dronecategory-list) and returns the response generated by this request.

The code uses the self.client attribute to access the APIClient instance that allows us to easily compose and send HTTP requests for testing our RESTful Web Service that uses the Django REST framework. For this test, the code calls the post method with the built url, the data dictionary, and the desired format for the data: 'json'.

Many test methods will call the post_drone_category method to create a new drone category and then compose and send other HTTP requests to the RESTful Web Service. For example, we will need a drone category to post a new drone.

The test_post_and_get_drone_category method tests whether we can create a new DroneCategory and then retrieve it. The method calls the post_drone_category method and then calls assert many times to check for the following expected results:

  1. The status_code attribute for the response is equal to HTTP 201 Created (status.HTTP_201_CREATED)
  2. The total number of DroneCategory objects retrieved from the database is 1
  3. The value of the name attribute for the retrieved DroneCategory object is equal to the new_drone_category_name variable passed as a parameter to the post_drone_category method

The previously coded tests make sure that we can create a new drone category with the RESTful Web Service, it is persisted in the database, and the serializer does its job as expected. The drone category is a very simple entity because it just has a primary key and a name. Now, we will add more test methods that will allow us to cover more scenarios related to drone categories.

Add the test_post_existing_drone_category_name method to the recently created DroneCategoryTests class in the restful01/drones/tests.py file. The code file for the sample is included in the hillar_django_restful_10_01 folder in the restful01/drones/tests.py file:

    def test_post_existing_drone_category_name(self): 
        """ 
        Ensure we cannot create a DroneCategory with an existing name 
        """ 
        url = reverse(views.DroneCategoryList.name) 
        new_drone_category_name = 'Duplicated Copter' 
        data = {'name': new_drone_category_name} 
        response1 = self.post_drone_category(new_drone_category_name) 
        assert response1.status_code == status.HTTP_201_CREATED 
        response2 = self.post_drone_category(new_drone_category_name) 
        print(response2) 
        assert response2.status_code == status.HTTP_400_BAD_REQUEST 

The new method tests whether the unique constraint for the drone category name works as expected and doesn't make it possible for us to create two drone categories with the same name. The second time we compose and send an HTTP POST request with a duplicate drone name, we must receive an HTTP 400 Bad Request status code (status.HTTP_400_BAD_REQUEST).

Add the test_filter_drone_category_by_name method to the DroneCategoryTests class in the restful01/drones/tests.py file. The code file for the sample is included in the hillar_django_restful_10_01 folder in the restful01/drones/tests.py file:

    def test_filter_drone_category_by_name(self): 
        """ 
        Ensure we can filter a drone category by name 
        """ 
        drone_category_name1 = 'Hexacopter' 
        self.post_drone_category(drone_category_name1) 
        drone_caregory_name2 = 'Octocopter' 
        self.post_drone_category(drone_caregory_name2) 
        filter_by_name = { 'name' : drone_category_name1 } 
        url = '{0}?{1}'.format( 
            reverse(views.DroneCategoryList.name), 
            urlencode(filter_by_name)) 
        print(url) 
        response = self.client.get(url, format='json') 
        print(response) 
        assert response.status_code == status.HTTP_200_OK 
        # Make sure we receive only one element in the response 
        assert response.data['count'] == 1 
        assert response.data['results'][0]['name'] == 
drone_category_name1

The new method tests whether we can filter a drone category by name, and therefore, checks the usage of the filter field we have configured for the DroneCategoryList class-based view. The code creates two drone categories and then calls the django.utils.http.urlencode function to build an encoded URL from the filter_by_name dictionary. This dictionary includes the field name as a key and the desired string for the field as a value. In this case, drone_category_name1 is equal to 'Hexacopter', and therefore, the encoded URL saved in the url variable will be 'name=Hexacopter'.

After the call to self.client.get with the built URL to retrieve the filtered list of drone categories, the method verifies the data included in the response JSON body by inspecting the data attribute for the response. The second line that calls assert checks whether the value for count is equal to 1 and the next lines verify whether the name key for the first element in the results array is equal to the value hold in the drone_category_name1 variable. The code is easy to read and understand.

Add the test_get_drone_categories_collection method to the DroneCategoryTests class in the restful01/drones/tests.py file. The code file for the sample is included in the hillar_django_restful_10_01 folder in the restful01/drones/tests.py file:

    def test_get_drone_categories_collection(self): 
        """ 
        Ensure we can retrieve the drone categories collection 
        """ 
        new_drone_category_name = 'Super Copter' 
        self.post_drone_category(new_drone_category_name) 
        url = reverse(views.DroneCategoryList.name) 
        response = self.client.get(url, format='json') 
        assert response.status_code == status.HTTP_200_OK 
        # Make sure we receive only one element in the response 
        assert response.data['count'] == 1 
        assert response.data['results'][0]['name'] == 
new_drone_category_name

The method tests whether we can retrieve the drone categories collection. First, the code creates a new drone category and then makes an HTTP GET request to retrieve the drones collection. The lines that call assert check that the results include the only created and persisted drone and that its name is equal to the name used for the call to the POST method to create the new drone category.

Add the test_update_drone_category method to the DroneCategoryTests class in the restful01/drones/tests.py file. The code file for the sample is included in the hillar_django_restful_10_01 folder in the restful01/drones/tests.py file:

    def test_update_drone_category(self): 
        """ 
        Ensure we can update a single field for a drone category 
        """ 
        drone_category_name = 'Category Initial Name' 
        response = self.post_drone_category(drone_category_name) 
        url = reverse( 
            views.DroneCategoryDetail.name,  
            None,  
            {response.data['pk']}) 
        updated_drone_category_name = 'Updated Name' 
        data = {'name': updated_drone_category_name} 
        patch_response = self.client.patch(url, data, format='json') 
        assert patch_response.status_code == status.HTTP_200_OK 
        assert patch_response.data['name'] == 
updated_drone_category_name

The new method tests whether we can update a single field for a drone category. First, the code creates a new drone category and then makes an HTTP PATCH request to update the name field for the previously persisted drone category. The lines that call assert check that the returned status code is HTTP 200 OK and that the value of the name key in the response body is equal to the new name that we specified in the HTTP PATCH request.

Add the test_get_drone_category method to the DroneCategoryTests class in the restful01/drones/tests.py file. The code file for the sample is included in the hillar_django_restful_10_01 folder in the restful01/drones/tests.py file:

    def test_get_drone_category(self): 
        """ 
        Ensure we can get a single drone category by id 
        """ 
        drone_category_name = 'Easy to retrieve' 
        response = self.post_drone_category(drone_category_name) 
        url = reverse( 
            views.DroneCategoryDetail.name,  
            None,  
            {response.data['pk']}) 
        get_response = self.client.get(url, format='json') 
        assert get_response.status_code == status.HTTP_200_OK 
        assert get_response.data['name'] == drone_category_name 

The new method tests whether we can retrieve a single category with an HTTP GET request. First, the code creates a new drone category and then makes an HTTP GET request to retrieve the previously persisted drone category. The lines that call assert check that the returned status code is HTTP 200 OK and that the value of the name key in the response body is equal to the name that we specified in the HTTP POST request that created the drone category.

Each test method that requires a specific condition in the database must execute all the necessary code to generate the required data. For example, in order to update the name for an existing drone category, it was necessary to create a new drone category before making the HTTP PATCH request to update it. Pytest and the Django REST framework will execute each test method without data from the previously executed test methods in the database, that is, each test will run with a database cleansed of data from the previous tests.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset