Test Driven Development: Django Rest Project

Johanes Marihot Perkasa Simarmata
12 min readMar 18, 2022

--

What is Test Drive Development? What are the advantages and weaknesses? What are the steps? How to do it in our project?

Source: https://miro.medium.com/max/475/1*Mjb3IFooRmFumA2IgNEWbw.png

“The best Test Driven Development can do, is assure that code does what the programmer thinks it should do. That is pretty good by the way.” — James Grenning, Founder of Wingman Software

As a developer or programmer, when creating a system, we definitely want our product is in accordance with the wishes of stakeholders, smooth to use, and bug free. Therefore, one of the keys to realizing these expectations is to know and do a Test Driven Development (TDD).

What is Test Driven Development?

Test Driven Development is a methodology that ensuring us about every component that we develop will run properly in our system. Test Driven Development (TDD) starts by designing and developing test for every small functionality of our application. Following this method, we are required to write test cases first before the code is written and refactored so as to write a new code only if the automated test fails.

Advantages of Using TDD

  • Code more clearly, simple, and free of bug
  • Avoid code duplication
  • TDD not only helps identify problems, it also helps prevent problems
  • TDD will help programmers to think critically about the requirements needed and the code design that will be developed
  • Do a TDD provides a faster time in merging a system

Weaknesses of Using TDD

  • If there is a requirement that changes, then we will most likely create a new set of tests
  • TDD focuses on Developer expertise and has little interaction with Stakeholders/Customers
  • In certain situations, if the system works properly according to the expectations of customers or stakeholders, then no detailed TDD is needed

Convincing Your Team to Use TDD

To encourage people to accept that TDD might be a way they could work more effectively, we can help them see some of its potential benefits:

  • Preventing logic defects from getting integrated
  • Diminishing the time spent in debugging
  • Supporting the ability to keep the codebase well-organized and readable
  • Minimizing redundant amounts of code
  • Promoting the ability to deliver frequently and with high confidence
  • Providing a simple rhythm that helps developers continually and consistently make progress
  • Creating a body of tests that act as living documentation on intended capabilities in the code

What if your friend doesn’t want to do it?

  • Apply TDD yourself. You have to be the change here. That might not be enough, but it is vital to do
  • Do some pairing with a willing guinea pig The best way to teach TDD in my experience is for you to do some pair programming. You write the test. Get your colleague to write the production code to make it pass.
  • Prefer less unit tests to start with. Just encourage the team to get going with one test. If that is the only one that class gets checked in with, you are making a transition. You can improve on this later.

Stages of TDD

  • Red stage
    Programmer needs to make sure if he/she has understood about the functionality required. Programmer must write the test cases without any code in front of him/her and run them all. As there is no code, you will get compile error and it is referred as the red stage indicating failure of test cases.
  • Green stage
    In this stage, developer only writes the minimum required code to pass the test cases just got failed. We need to ensure that no extra code is going in and all old test cases also pass. It doesn’t even need to be effective or implement clean code, the main goal is that the code can pass the test.
  • Refactor
    This stage tells programmer to refactor the code to ensure the functionality is intact and code is refined. Some aspects that can be achieved are efficient design, maintainable code, and clean code. We can refactor many times until it becomes the best code as expected. In the refactor, we must also ensure that the code that is created must pass the tests that were previously successful so that there is no change in the code test from green to red again.

Example of Using TDD

Before we start, I will give you a little introduction about what I will make. In this Django application, I want to make a CRUD application that describes the relationship between courses, curriculum, and year of classes in my faculty. A curriculum consists of many courses and 1 course could be in many curriculums too, so they have a many to many relationship. A year of class has only 1 curriculum but one curriculum could be used by many of year classes, so they have a one to many relationship (in this case, we will use foreign key as the relation model). But in this article, I will explain and test about curriculum and year of classes because course is not given as my task part but it’s for my project’s partner. First of all, we will create tests for the model first:

  • As a note for us, if we want to create a test, we should create a file or a function with a name starting with a test. For example, ‘tests.py’ for the name of the file and ‘test_hello_world ’for the name of the function. To complete the testing activity, we must know what coverage is. Coverage indicates how much the tests that have been made cover the code that we have created as well as a benchmark for the completeness of the function, the success of the function implementation, and also the cleanliness of the code. Here is how to check it in your project (I’m assuming you’ve already installed:
coverage run — omit=’manage.py’ — include=”FOLDER_NAME/*” manage.py testcoverage report -m

Model Testing: Red Test for Curriculum (Kurikulum)

Test for Kurikulum Model (RED)

The image above shows use that I wanted to test a model called by Kurikulum (Curriculum) and I would have three fields on it which are nama as a representation of the curriculum’s name, jurusan as a representation of major’s target of the curriculum, and mata_kuliah (course) as the part of curriculum. In this case, I provided 2 classes which are the setup and the test model. In the setup, I wrote 4 variables which are mataKuliahData that consists of the course’s data we wanted to store and mataKuliah which is the MataKuliah object, kurikulumData that consists of curriculum’s data that we wanted to store, and kurikulum which is the ‘Kurikulum’ object that we wanted to create. What I want to test here is that when my curriculum object is called, I expect a string to be returned according to the format I gave in the code.

This is the commit and the pipeline status:

Commit status for the red test

The image above shows us that my test is failed because of Kurikulum is not defined and I haven’t create and import the model to the test file. You can see what the commit message in the image above, I knew that the test would be red because of an error that I’ve tried in local, so I gave a commit message consist of a ‘RED’ as a sign for the pipeline that would be red.

Model Testing: Green Test for Curriculum (Kurikulum) model

Implement Kurikulum model to pass the test
Pipeline status Green for Kurikulum

We can see in models.py I created a Curriculum class with the attributes I mentioned earlier. But the main thing here is to make the test green as I included in the code in line 15 so that the output that comes out when the object is called is in accordance with what I included in the test

Model Testing: Red Test for Year of Class (Angkatan) model

Test for Angkatan Model
Test Result
Pipeline Status Red for Angkatan

This Test Model is similar to the previous one, but the attributes used are slightly different. The attributes used here are kurikulum (curriculum), tahun (year), and jurusan (major). However, the thing that is tested is the same, namely when this object is called z.

Model Testing: Green Test for Year of Class (Angkatan) model

Implement Angkatan model to pass test
Pipeline Status Green for Angkatan

Just like before, we define the required attributes in the test and create a string return to match what is expected in the test.

Now, I want to share about testing of endpoints with combination of Django Rest Framework which is supported with authentication tokens and user roles (admin and student). Since the two models that I made are CRUD, then I will only explain for one of them, which is Kurikulum.

Before I start, I’m assuming that you understand the concept of django authentication using tokens like how to create and use them.

Setup User data for Mahasiswa (Student) and Admin

User data setup

We can see that in the image above I already prepared the data for student and admin respectively. I have registered and logged in to both accounts so that from the login results, I will get a token which will be the one of the requirements to call the endpoints. So, how to use the token? Let see the image below

Function to provide token in every single request

Create Kurikulum Endpoint: Red Test

URLs Endpoint for testing
Test cases when creating Kurikulum

If you ask, what do I want to test in the picture above? Let me explain:

  • In the first test, I want to show that create curriculum was successfully created with the expectation that the response status code is 201 (Created). This can be achieved if the user is an admin (identified by the token) and the fields filled in are complete and matched.
  • In the second test, I want to show that create curriculum was failed to be created. This fails because the token identifies that the role of the user is not admin even though the fields filled in are correct and complete. Therefore, I expect the status code of the response to be 403 (Forbidden).
  • In the last test, I want to show that create curriculum was failed to be created too. This fails because the fields are not filled correctly and completely even the token identifies that user role is admin. Therefore, I expect the status code of the response to be 400 (Bad Request).

Create Kurikulum Endpoint: Green Test

API View for Kurikulum when Post

Maybe I should explain some that I haven’t explained before: Authentication_classes indicates the type of authentication that we use, namely token. Permission_classes indicates that what the user needs if they want to access an endpoint is IsAuthenticated. Is_admin indicates that whether a user who accesses the endpoint is admin or not (is_admin is a custom of our authentication). Serializer is a model that is used to convert data from one data type to another and can validate incoming data, vice versa. In this case, we use a serializer called by KurikulumCreateUpdateSerializer which we will use when creating and updating kurikulum.

URLs for the endpoint

The image above shows us how to do an API call and directing to the view that we’ve created which is KurikulumView.

Get Kurikulum Endpoint: Red Test

Test Cases when getting a/all Kurikulum(s)
Pipeline status for the test cases of getting a/all Kurikulum(s)

Let me explain what I want to do with the test:

  • In the first test, I want to do a test to get a curriculum with a certain id and authenticate as an admin. What I hope is that the response data is the same as if we serializer it manually and the response status is 200 (OK).
  • In the second test, the desired thing is the same as the first one, but the user is a student. In the case of a GET request, I allow both admins and non-admins (students) to be able to access curriculum data.
  • In the third test, I want to do a test to get all curriculum objects without filtering by id and authenticated as admin. What I expect is also the same as the first one, namely the appropriate response data and status.
  • In the last test, the desired thing is the same as the third one, but the user is a student.

So, how to make it to a green?

Get Kurikulum Endpoint: Green Test

API View for Kurikulum when Get
Green status for the test cases

What’s new here? If you don’t know what does pk refer to? Pk stands for primary key of an object. So, if we define the pk in our URL, it will return a Kurikulum object that has the pk. But if we don’t, it will return all objects of Kurikulum without any filter. The serializer is a bit different because I want to integrate the new serializer with mata kuliah (course), so I separate it when we create or update an object.

Update Kurikulum Endpoint: Red Test

Test Cases when updating a Kurikulum
Pipeline status for the test cases of updating Kurikulum

These are the explanations:

  • In the first test, I wanted to update an existing curriculum with new data. I’m authenticated as an admin so I should be allowed to update the object. After successfully updating, I expect that the response data will have the data I just entered which are the new name (nama) and new major (jurusan) and the response status code, which is 200 (OK).
  • In the second test, the same as the first goal. But, I’m authenticated as a student, so I’m expecting a Forbidden status response (403).
  • In the third test, I’m authenticated as admin and want to update a curriculum object with a new data. However, the data I provided was invalid because I added ‘self.mataKuliah.pk + 1’ which I had never created before so there would be an error with the response status code, which is 400 (BAD REQUEST).

Update Kurikulum Endpoint: Green Test

API View for Kurikulum when Update (Put)
Green status for the test cases

Nothing new here, it’s similar to the post one.

Delete Kurikulum Endpoint: Red Test

Test Cases when deleting a Kurikulum
Pipeline status for the test cases of deleting Kurikulum

These are the explanations of the test:

  • In the first test, I wanted to delete a pre-existing curriculum by authenticating as an admin. When the endpoint has been called, I expect a response that is ‘Item Deleted’ and the response status code is 200 (OK)
  • In the second test, the goal is the same, but I am authenticated as a student. Therefore, I expect that when the endpoint is called, it will return a response status code of 403 (Forbidden)

Refactor

Although all the codes has passed the test, it doesn’t mean our code is effective and efficient and implemented clean code. So what we need to do is refactoring. We must remember that in the refactor phase, all the things we change should not change the test results (eg from green to red again). Here is the example:

Refactoring tests.py

In this case, I did a clean code by lint in python. Things we can fix in our code are code that is too long in a line, code structure that is not good (such as the use of commas that don’t fit properly), no new line at the end of the file, incorrect indentation, unused imports, and other. Maybe if you want to know the tutorial of how to do it, this is my reference:

Tutorial to lint in VsCode

Also, in the refactor phase, you can check for bugs, vulnerabilities, bad security, code smell, duplication, and coverage. We can do this using sonarqube. We can access sonarqube with docker for one of the ways and then run it in our local environment to do a check on our code. By implementing this, the code becomes easier to read, maintain, and clean. Here is the example of my project:

Those are all the explanations of Test Driven Development on Django Rest Project based on what I understand, do, and read from the sources listed below. Hopefully this article is useful for all of us. Thank you for reading!

Reference

--

--