Automated Testing of a REST API - Part One

Michele Saba
A free video tutorial from Michele Saba
Developer, Instructor, Consultant
4.6 instructor rating • 4 courses • 14,097 students

Lecture description

Learn how to use Django REST Framework to write Automated Tests for your REST APIs and Web Applications!

"Code without tests is broken as designed." — Jacob Kaplan-Moss

Learn more from the full course

The Complete Guide to Django REST Framework and Vue JS

Build Professional REST APIs and Single Page Applications with Django and Vue JS !

20:55:07 of on-demand video • Updated October 2021

  • How to build Backend REST APIs with Python & Django
  • How to use Django REST Framework and Vue JS to create powerful Single Page Applications, similar to those used by Google, Instagram and Twitter
  • How to build professional Production-Ready REST APIs with Python, Django and Django REST Framework
  • How to secure the REST APIs you will create with both Token and Session Authentication
  • All the basics of Vue JS and Vue CLI for creating reactive Components and Single Page Applications
  • How to create Real-World Single Page Applications with Vue JS and Django
English Welcome to this new practical lesson about automated testing in Django REST Framework! Let's jump right into it. Here we are in Visual Studio Code and as you can see I'm currently in the tests.py file of our Profiles Application. As you probably already know writing automated tests for your application is always a good idea. First of all as their name clearly states they allow you to check, to test, every single component of your application in an automated way so that by running these automated tests you can be sure that everything in your app is working as expected. They also allow you to easily future proof your application because no matter the changes that you might apply to your code base you will always be able to check if your application is behaving as intended in the first place just by running these tests. So let's make some examples of how we can easily integrate them in our Django REST Framework projects. First of all we need to import some classes and functions and we've got quite a few to import. We don't need Django's TestCase class because Django REST Framework provides us a specific ApiTestCase class. So first of all, import Json. Let's import Django's User Model from django.contrib.auth.models And then the reverse function. then from rest_framework.authtoken.models import Token. Let's also import the APITestCase class. from rest_framework import status and then of course we need to import our models and serializers. So from profiles.models import profile and from profiles.api.serializers import ProfileSerializer. and I also give you an extra tip. In cases like this where you've got several import declarations, if you want to check that everything is in proper alphabetical order. You can use one of these one of visual studio code's functionalities, just right click and then sort imports. And even though everything was already in order it added a couple of void spaces just to make it a little bit more readable. The first thing that we're now going to check is the registration End Point. class RegistrationTestCase which extends APITestCase And here we can define a method def test_registration which accepts self as always. And here we can define a data dictionary with all the data that we want to use to create a new user instance. So first of all of course we're going to need username and we can just call it test case. Now we're going to need an email. We can set something like test at local host dot app then we're going to need password one which we can set as some strong password and password to like. So now we can use the client provided by APITestCase to make a post request to the registration end point and check if everything is working as expected. Pretty easy to do. Response equals self.client.post. So first of all the endpoint and then the data that we want to send. To be sure that everything worked as expected, We can now check the response status code and if the status code is 201 created we know that everything is working as expected. So self.assertequal. So first of all response.statuscode and we want to compare it to status.HTTP_201_CREATED created We also actually need to add a forward slash here at the beginning We can now run our development server. Let's open up another terminal and we can now run Python manage.py test. Create and test database for alias default. And we see that everything worked just as expected we get an OK run one test in zero point one eight five seconds. We can now be sure that our registration endpoint is working as expected. However we see this created true and created false which as you probably remember are called or rather executed by your create profile function in the Signals.py file. I'm going to comment this out like so. And if we now run the test once again we see that the two created are no longer here. One important thing to notice or to remind is that automated tests do not use the let's call it "standard database" that you are using in development but as we can read they create a specific database that is used exclusively in the context of testing your application. And this is really awesome because most of the times you will want to work with a database to test your application and therefore of course having a new one for free every time you want to run your tests is great. Let's now build the second Test Case class for this first part of the automated testing lesson and this is going to be profile view set test case which of course is going to extend APITestCase. Considering that the endpoint provided by our profile viewset class required us to be authenticated in order to use them the first method that we want to create within our test case class is going to be set up and within set up we're going to create a new user object instance. That's what I've been going to use to authenticate ourselves but also to check how authentication is actually working in the profiles app itself. Def set up, self and here we can define self.user equals user.objects.create_user. And we need to pass first of all username. We can set something like "davinci" then we need the password so password equals some very strong password. We now need to create a token to use with the user so self.token equals token.objects.create with user equals self.user. Now to authenticate ourselves we need to define another method. Def API authentication self. And here we can set self.client.credentials. And here we can pass HTTP authorization equals token space plus self.token. And we can now call API authentication from within setup. So self.api_authentication. Now that the setup of our class is completed we can test the profile list endpoint. And considering that we are going to reference the list url several times we can just define list_url as reverse of profile list. And in case you're wondering we can go back to the API views.py file right here. And so you see that our profile view set has defined queryset as profile.objects.all Therefore the router class knows that the list end point has to be called profile-list which is basically a standard. Always remember that you can clear any doubts you might have by checking django REST framework's documentation and the source code of classes and functions you're using. So let's go back to the tests.py file right here. Now Iam going to define def test profile list authenticated And here we can define response as the self.client.get and here self.assert equals response.status_code, statu.HTTP_200_OK. Okay. We can also define sort of a complementary method so test_profile_list_un_authetnticated And here first of all we need to de authenticate our user and it is in fact pretty easy. We can use the client once again self.client.force_authenticate with user equals None. And this time of course we want to check for a different status code In this case 403_FORBIDDEN. We can now run our tests once again, so I'm just going to clear, test And we actually get an error you see, which is a type error, can only concatenate string not token to strings, because in fact this is a self.token.key. We can launch the tests once again and this time of course everything works fine which tells us that our profile list endpoint is robust and secure. Lets now test the detail endpoint so def test profile. Detail. Retrieve, self. And here we can define. Response equals self.client.get We are going to use reverse of profile detail, again for the same principles that we've described here for Profile List. But this time you also want to pass a keyword arguments dictionary with primary key one. Basically want to get the details of our DaVinci user. First of all we can check the status code, like so, but this time the status code is not enough. We also want to check if the details that we are getting back from the endpoint correspond to what we might expect. So for example self.assertEqual, response.data, user, and we want to compare it to davinci, which we know is the name of the user. Let's now run the tests once again. So, test. Everything fine. Let's go check the update capabilities. So let's check. Def test profile update by owner. And now we can test if, being authenticated as DaVinci we are able to update the user profile information so we can actually copy this one, but, this time the method is going to be PUT and we need to pass quite a lot more of data. So "city", "anchiano" and biography And here we can said Renaissance Genius. self.assertEqual we check the status code once again like so, and here we want to check the whole JSON response so self.assertEqual. JSON.loads, with response.content, and so here ID, 1, user, "davinci" biography renaissance genius. Then we can add the city like so and avatar None. We can also test what happens if a random users tries to update the same profile instance. So I can just copy the code like so, but this is going to be test profile update by random user. So we do not need this code in here. First of all random user equals user.objects.create_user with username equals random and password equals password. One two three one two three one two three. Which of course is to be a string. Then we can use force_authenticate here I can just copy this line. Like so, with user equals random user. And we could just try to update the biography with something like packed with three exclamation marks and if everything works as expected we just want to return. HTTP_403_FORBIDDEN which is therefore what we expect to get as a response. So let's now test everything once again. terminal Like so and everything works as expected. Awesome! (see you in part two!)