Writing new unit tests to improve the tests' code coverage

Our first round of unit tests was related to the drone category class-based views: DroneCategoryList and DroneCategoryDetail. Now, we will write a second round of unit tests related to the pilot class-based views: PilotList and PilotDetail. The new tests will be a bit more complex because we will have to work with authenticated requests.

In Chapter 17, Securing the API with Authentication and Permissions, we configured authentication and permission policies for the class-based views that work with the Pilot model. We overrode the values for the authentication_classes and permission_classes class attributes for the PilotDetail and PilotList classes. In order to create, read, update, or delete pilots, we have to provide an authentication token. Hence, we will write tests to make sure that an unauthenticated request cannot perform operations related to pilots. In addition, we want to make sure that an authenticated request with a token can create a new pilot and then retrieve it.

Open the restful01/drones/tests.py file and add the following lines after the last line that declares the imports, before the declaration of the DroneCategoryTests class:

from drones.models import Pilot 
from rest_framework.authtoken.models import Token 
from django.contrib.auth.models import User 

Add the following code to the existing restful01/drones/tests.py file to create the new PilotTests class. The code file for the sample is included in the hillar_django_restful_10_02 folder in the restful01/drones/tests.py file:

class PilotTests(APITestCase):    
    def post_pilot(self, name, gender, races_count): 
        url = reverse(views.PilotList.name) 
        print(url) 
        data = { 
            'name': name, 
            'gender': gender, 
            'races_count': races_count, 
            } 
        response = self.client.post(url, data, format='json') 
        return response 
 
    def create_user_and_set_token_credentials(self): 
        user = User.objects.create_user( 
            'user01', '[email protected]', 'user01P4ssw0rD') 
        token = Token.objects.create(user=user) 
        self.client.credentials( 
            HTTP_AUTHORIZATION='Token {0}'.format(token.key)) 
 
    def test_post_and_get_pilot(self): 
        """ 
        Ensure we can create a new Pilot and then retrieve it 
        Ensure we cannot retrieve the persisted pilot without a token 
        """ 
        self.create_user_and_set_token_credentials() 
        new_pilot_name = 'Gaston' 
        new_pilot_gender = Pilot.MALE 
        new_pilot_races_count = 5 
        response = self.post_pilot( 
            new_pilot_name, 
            new_pilot_gender, 
            new_pilot_races_count) 
        print("nPK {0}n".format(Pilot.objects.get().pk)) 
        assert response.status_code == status.HTTP_201_CREATED 
        assert Pilot.objects.count() == 1 
        saved_pilot = Pilot.objects.get() 
        assert saved_pilot.name == new_pilot_name 
        assert saved_pilot.gender == new_pilot_gender 
        assert saved_pilot.races_count == new_pilot_races_count 
        url = reverse( 
            views.PilotDetail.name,  
            None, 
            {saved_pilot.pk}) 
        authorized_get_response = self.client.get(url, format='json') 
        assert authorized_get_response.status_code == 
status.HTTP_200_OK assert authorized_get_response.data['name'] == new_pilot_name # Clean up credentials self.client.credentials() unauthorized_get_response = self.client.get(url, format='json') assert unauthorized_get_response.status_code ==
status.HTTP_401_UNAUTHORIZED

The PilotTests class is a subclass of the rest_framework.test.APITestCase superclass and declares the post_pilot method that receives the desired name and gender for the new pilot as arguments.

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

Many test methods will call the post_pilot method to create a new pilot and then compose and send other HTTP requests to the RESTful Web Service. Notice that the post_pilot method doesn't configure authentication credentials, and therefore, we will be able to call this method for unauthenticated or authenticated users. We already know that unauthenticated users shouldn't be able to post a pilot, and a test will call this method without a token and make sure no pilot is persisted in the database.

The create_user_and_set_token_credentials method executes the following actions:

  • Creates a Django user with a call to the User.objects.create_user method.
  • Creates a token for the previously created Django user with a call to the Token.objects.create method.
  • Includes the token generated for the Django user as the value for the Authorization HTTP header key with the 'Token ' string as a prefix for the token. The last line calls the self.client.credentials method to set the generated HTTP header as the value for the HTTP_AUTHORIZATION named argument.
Remember that the self.client attribute allows us to access the APIClient instance.

Whenever a test wants to perform an HTTP request with a token, the code will call the create_user_and_set_token_credentials method. In order to clean up the credentials configured for the APIClient instance saved in self.client, it is necessary to call the self.client.credentials() method without arguments.

The test_post_and_get_pilot method tests the following path:

  1. We can create a new Pilot with an HTTP POST request that has an appropriate authentication token
  2. We can retrieve the recently created Pilot with an HTTP GET request that has an appropriate authentication token
  3. We cannot retrieve the recently created Pilot with an unauthenticated HTTP GET request

The code calls the create_user_and_set_token_credentials method and then calls the post_pilot method. Then, the code 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 Pilot objects retrieved from the database is 1
  1. The value of the name, gender, and races_count attributes for the retrieved Pilot object is equal to the values passed as parameters to the post_pilot method

Then, the code calls the self.client.get with the built URL to retrieve the previously persisted pilot. This request will use the same credentials applied to the HTTP POST request, and therefore, the new request is authenticated by a valid token. The method verifies the data included in the response JSON body by inspecting the data attribute for the response. The code calls assert twice 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 value of the name key in the response body is equal to the name that we specified in the HTTP POST request

Then, the code calls the self.client.credentials method without arguments to clean up the credentials and calls the self.client.get method again with the same built URL, this time, without a token. Finally, the code calls assert to check that the status_code attribute for the response is equal to HTTP 401 Unauthorized (status.HTTP_401_UNAUTHORIZED).

The previously coded test makes sure that we can create a new pilot with the RESTful Web Service and the appropriate authentication requirement we configured, the pilot is persisted in the database, and the serializer does its job as expected. In addition, unauthenticated users aren't able to access a pilot.

Add the test_try_to_post_pilot_without_token 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_02 folder in the restful01/drones/tests.py file:

    def test_try_to_post_pilot_without_token(self): 
        """ 
        Ensure we cannot create a pilot without a token 
        """ 
        new_pilot_name = 'Unauthorized Pilot' 
        new_pilot_gender = Pilot.MALE 
        new_pilot_races_count = 5 
        response = self.post_pilot( 
            new_pilot_name, 
            new_pilot_gender, 
            new_pilot_races_count) 
        print(response) 
        print(Pilot.objects.count()) 
        assert response.status_code == status.HTTP_401_UNAUTHORIZED 
        assert Pilot.objects.count() == 0

The new method tests that the combination of permission and authentication classes configured for the PilotList class doesn't make it possible for an unauthenticated HTTP POST request to create a pilot. The code calls the post_pilot method without configuring any credentials, and therefore the request runs without authentication. Then, the code calls assert twice to check for the following expected results:

  1. The status_code attribute for the response is equal to HTTP 401 Unauthorized (status.HTTP_401_UNAUTHORIZED)
  2. The total number of Pilot objects retrieved from the database is 0 because the received data to create a new pilot wasn't processed

We have increased the scenarios covered by our tests. We should write more tests related to pilots. However, with all the examples provided, you will have the necessary information to write all the tests required to make sure that each new version of a RESTful Web Service developed with Django and the Django REST framework works as expected.

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

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