Published on

Unit testing Django applications using Pytest!

What's the one of the most important and often overlooked parts of software applications from web applications, mobile applications to Cloud Native application? Unit tests of course! Unit tests are an integral part of any software application because they allow for the detection of bug/defects, improve maintainability of code and aid in regression testing when making updating existing code-bases.

Diagram detailing the unit testing process!

Figure 1: Diagram detailing the unit testing process!

In this blog post, we will show you guys how to write basic unit test using Pytest for Django applications so without further ado, let's jump right in!


To begin unit testing with Pytest we will need the pip package manager installed. If you don't have pip installed then refer to this article by on how to do so.

Now with pip installed, we can install pytest via the following command:

pip install -U pytest

Then as a sanity check, running the following command to see the version of pytest that was installed:

pytest --version

Next we'll need a sample python application to run our basic unit tests on. If you don't have one, feel free to clone this simple blog application here, as this is the application to be used in this blog post.

Then, make sure to activate a virtual environment when in the root folder of this project by running the following command:

python -m venv virtual_env

As the previous command created the virtual environment folder, we now have to activate it using this command:

source virtual_env/Scripts/activate # for Windows users
source virtual_env/bin/activate # for Linux and Mac users

Great! You're now in a Python virtual environment. Moving on to the testing section...


To begin let's start out by creating a 'tests' folder in the root folder. After this, let's create a file called inside the newly created 'tests' folder.

When beginning the unit testing process it is important to have a file to hold configuration data. When a single file holds the logic for configuring the unit tests, we enforce the DRY principle by reducing code and logic duplication.

Inside the file, let's add the following code:

import pytest
import factory
from pytest_factoryboy import register
from rest_framework.test import APIClient, APIRequestFactory
from django.contrib.auth import get_user_model
from forum.models import ForumUser
from django.contrib.auth.hashers import make_password
from django import VERSION as DJANGO_VERSION
from django.test import TestCase
from django.urls import reverse

class ConfTest(TestCase):

    def test_register_success(self):
        superuser = User.objects.create_superuser(
        assert User.objects.count() > 0

    def create_test_user(self):
        test_user = User.objects.create(
        return test_user

Here's an explanation of the what we just added:

  • test_register_success(): First we create a superuser(to learn more about Django superusers read this article) and persist that user to the database. Then we query the database for that user using the assert statement assert User.objects.count() > 0, which proves that the newly created superuser is saved to the database successfully
  • create_test_user(): This function is used to create a new User instances whenever the need arises to do so. For example when persisting a Post instance an User instance is required for it's author field, therefore using this function to get an User is a good use of the DRY principle(to learn more about the DRY Principle click here)

Next, let's move on to testing the models in this application. To do so, first create a new file inside the tests folder called then add the following code to it:

import pytest
from django.contrib.auth.models import User
from django.test import TestCase
from blogapp.models import Category, Post
from conftest import ConfTest

class ModelTest(TestCase):

    def test_category_model_persists(self):
        new_category = Category() = "new_category"

        assert == "new_category"
        assert Category.objects.count() > 0

    def test_post_model_persists(self):
        new_post = Post()
        new_post.title = "new_post" = ConfTest.create_test_user(self)

        old_post_obj_count = Post.objects.filter().count()
        new_post_obj_count = Post.objects.filter().count()

        assert new_post.title == "new_post"
        assert old_post_obj_count < new_post_obj_count

Now for a breif explanation as to what we have just added:

  • test_category_model_persists(self): This test case is for testing the ability of the Category class to save instances of itself
  • test_post_model_persists(self): This test case is similar to the one above with the addition of having to create an User object, as the author field requires a foreign key of type User(to learn more about foreign keys click here)

Now let's move on to doing some basic unit testing for the routing in the application.

First, start out be creating a new file in the tests folder called Then populate it with the following code:

import pytest
from django.contrib.auth.models import User
from django.test import TestCase
from blogapp.models import Category, Post
from django.urls import reverse

class RouteTest(TestCase):

    def test_homepage(self):
        homepage_url = reverse("blog:MainView")

        response = self.client.get(homepage_url)

        assert response.status_code == 200
        assert response.context["request"].path == "/"

    def test_no_access_page(self):
        no_access_url = reverse("blog:NoAccess")

        response = self.client.get(no_access_url)

        assert response.status_code == 200
        assert response.context["request"].path == "/noaccess/"

Now for an explanation of what we just added:

  • test_homepage(): Tests whether we are able to navigate to the homepage upon going to the route: "/". Notice that the function reverse() is used to provide much of the same functionality as the well known url() method, in that it acts as a generator and alias for urls defined in the config/ file(for more info on the reverse() function check out it's Django documentation page)
  • test_no_access_page(): Very similar to the test_homepage() test cases, with the exception of testing /noaccess/ instead of /

Finally, let's add one more set of unit tests for the Post objects in the file. Let's start by creating a new file called in the tests folder. After that just add the following code to our newly created file:

import pytest
from django.contrib.auth.models import User
from django.test import TestCase
from blogapp.models import Category, Post
from django.db.utils import IntegrityError
from conftest import ConfTest

class PostTest(TestCase):

    def test_post_saves_when_author_present(self):
        new_post = Post() = ConfTest.create_test_user(self)
        Post.objects.count() > 0

    def test_post_not_save_when_author_empty(self):
        new_post = Post()
        with self.assertRaises(IntegrityError):

    def test_new_post_has_zero_likes_after_saved(self):
        new_post = Post() = ConfTest.create_test_user(self)
        assert new_post.likes == 0

Now for an explanation of we just added:

  • test_post_saves_when_author_present(): This unit test primarily tests whether instances of the Post class can be saved when including a value for it's author field(which is not-null)
  • test_post_not_save_when_author_empty(): This unit test contrasts the one above in that the author field is intentionally left blank in order to trigger an IntegerityError. The assertion contained in this unit test expects this IntegerityError to be thrown when attempting to save the Post instance
  • test_new_post_has_zero_likes_after_saved(): This unit tests is similar to the one above with the slight twist of checking whether the likes field has a default value of 0 when it's Post instance object is persisted with a blank likes value

Now that we're done adding all our basic unit tests, let's run these test cases!

To do this just run the following command:

pytest -s

And voila! Here are the test results showing 9 successfully passing test cases:

9 passing unit tests!✔ ✔ ✔

Figure 2: 9 passing unit tests!✔ ✔ ✔


Congratulations! You now know how to make basic unit tests in your Django applications. If you need access to the source code for this application you can access it by visiting it's GitHub link.

Well that's it for this post! Thanks for following along in this article and if you have any questions or concerns please feel free to post a comment in this post and I will get back to you when I find the time.

If you found this article helpful please share it and make sure to follow me on Twitter and GitHub, connect with me on LinkedIn, subscribe to my YouTube channel.