• 10 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 8/30/22

Create Tests for the Django Framework Using Pytest-Django

To begin with, you need to install the pytest-django plugin so that you can use the Django framework to test a project. Use the command below:

pip install pytest-django

Create a Django Project

Now we need to create a little Django project before we can see how to create unit tests for the project. Don’t worry, I’ll give you all the commands you’ll need to create the project. Don’t forget to create a virtual environment before you start.

If you haven’t already done so, let’s install Django and the test modules:

pip install Django pytest-django

We can now create the Django project and an application that we’ll call  library :

django-admin startproject django_pytest 
cd django_pytest/
python manage.py startapp library

Next, add the new application to the  settings.py  file:

INSTALLED_APPS = [
   'django.contrib.admin',
   'django.contrib.auth',
   'django.contrib.contenttypes',
   'django.contrib.sessions',
   'django.contrib.messages',
   'django.contrib.staticfiles',
   'library', # <--- line to be added
]

We now need to add code to the project, so we’ll create a model and a view.

Add the following code to the  library/models.py  file:

from django.db import models
from django.utils import timezone

class Book(models.Model):
    author = models.CharField(max_length=50)
    title = models.CharField(max_length=50)

    def __str__(self):
        return f'{self.author} | {self.title}'

And in the  library/views.py  file, add the code that will display the contents of a book using the primary key of the book within the database. 

from django.shortcuts import get_object_or_404, render
from .models import Book

def book_infos(request, pk):
    book = get_object_or_404(Book, pk=pk)
    return render(request, "book_infos.html", {'book':book})

Now you can update the  url.py  file for the project:

from django.contrib import admin
from django.urls import path
from library import views      # <--- line to be added

urlpatterns = [
    path('admin/', admin.site.urls),
    path('<int:pk>', views.book_info, name="info") # <--- line to be added
]

Finally, to finish off the application code, you need to create a directory called  templates/  within the application directory where you’ll add your HTML file. Then, you’ll be able to create the  book_info.html  file in this directory to display book information.

Create the file  library/templates/book_info.html :

<p>{{ book.author }} | {{ book.title }}</p>

Once you’ve finished creating the application, you can migrate the database and launch the application.

The following command creates the migration or applies all new updates made based on the models:

python manage.py makemigrations

Next, you can run the migration using the following command:

python manage.py migrate

If you like, you can launch the application and test that everything’s working correctly:

python manage.py runserver

You’ve finished coding!

Configure Pytest-Django

Before creating your sequence of tests, create a  pytest.ini  file in the root directory that contains the  manage.py  file. This file contains the location for the project’s settings file (setting.py ). In our case, the  setting.py  file is located in the  django_pytest  directory.

Add the following to the  pytest.ini  file:

[pytest]
DJANGO_SETTINGS_MODULE = django_pytest.settings

You can also add the format of your test files to make it easier to run tests using the pytest command. Add another line with the format you’re using for your project ( tests.py ,  test_*.py  or  *_tests.py ).

Personally, I like to use the  test_*  format, so that’s the one I’m going to add:

python_files = test_*

Launch a Request

The Django framework provides a test client so that you can simulate HTTP requests to our application. You can access the test client by importing and instantiating as follows:

from django.test import Client
client = Client()

This client enables you to simulate your  GET  and  POST  requests by providing the path and the required data.

Create a GET Request

You can create a  GET  request using the following type of instruction:

client = Client()
client.get('/path/', {'key1': 'data1', 'key2': 'data2'})

This is the equivalent of a  GET  request using a URL:  /path/?key1=data1&key2=data2 .

Create a POST Request

In the same way, you can create a  POST  request using the following type of instruction:

client = Client()
client.post('/path/', {'key1': 'data1', 'key2': 'data2'})

This is the equivalent of a  POST  request using the URL  /path/  and an HTTP header containing  key1=data1&key2=data2 .

You now have two get()and  post() methods, enabling you to run  GET  and  POST  requests. The first argument must be the path and the second must be a dictionary containing any required data items.

Manage the Database

Before even starting to set up your first unit test for the project, you need to know how to manage the database elements. Don’t forget that you’re creating unit tests, so you mustn’t use the production database and you can’t create a database each time you want to run your tests.

Pytest provides functionality that will set up a temporary database that is built at test run time and then emptied at the end of the testing. You just need to create the required elements in your test database and then run your test scenario.

To do this, you need to specify the  @pytest.mark.django_db  decorator before defining your test.

Tests for a Django project can be placed either in a test folder structure at the project root or in each application’s directory.

Without further ado, you’re going to see how to use the following tests.

Test the URLs

First of all, you’re going to create a  tests/  folder within the  library  application to hold the full set of tests for the application. Then, you can create a  test_urls.py  file, which will hold the tests for the  urls.py  file.

The test will check that the URL path and view name are correct.

import pytest

from django.urls import reverse, resolve
from library.models import Book

@pytest.mark.django_db
def test_book_info_url():
    Book.objects.create(author = "Charles Dickens",
                        title = "Great Expectations")
    path = reverse('info', kwargs={'pk':1})

    assert path == "/1"
    assert resolve(path).view_name == "info"

Don’t worry, I’m going to explain each line of code to you:

  • Book.objects.create(author = "Charles Dickens", title = "Great Expectations") : Create a book using the  Book  model.

  • path = reverse('info', kwargs={'pk':1}) : Generate the URL using the name of the view passed as a parameter.

  • assert path == "/1" : Check if the URL is correct.

  • assert resolve(path).view_name == "info" : Check if the name of the view is correct and that the URL matches the view name.

Test the Views

Stay in the same test directory and add a test file to check the views (library/tests/test_views.py ).

You’re going to check that the content returned by the view and the HTTP status code are valid. Here’s the code you can use to check this:

import pytest

from django.urls import reverse
from django.test import Client
from library.models import Book
from pytest_django.asserts import assertTemplateUsed

@pytest.mark.django_db
def test_book_info_view():
    client = Client()
    Book.objects.create(author = "Charles Dickens",
                        title = "Great Expectations")
    path = reverse('info', kwargs={'pk':1})
    response = client.get(path)
    content = response.content.decode()
    expected_content = "Charles Dickens | Great Expectations”

    assert content == expected_content
    assert response.status_code == 200
    assertTemplateUsed(response, "book_info.html")

Right, now for a little explanation of these new lines of code:

  • client = Client() : Create a test client. This client will behave like a simplified browser. Note that there’s no need to start the Django server when using this test client.

  • response = client.get(path) : Request the retrieved URL using the  reverse()  function.

  • content = response.content.decode() : Decode the HTML response returned by the view.

  • expected_content = … : Specify the expected response. 

  • assert content == expected_content : Check that the view response and the expected response are the same.

  • assert response.status_code == 200 : Check that the HTTP status code is equal to 200. 

  • assertTemplateUsed(response, "book_info.html") : Check that the provided name template was used to generate the response.

Test the Models

You’re going to continue working in the test directory and add a new file to test the application models. In your case, you only have one model to test.

I’ve set up a test that will check the  Book  model. We can use the special function  __str__  from the  Book  class to check this. In our case, it will return information about the instance. We’re going to use the  create()  function to help us, because this will return the model instance at the point of insertion into the database.

Copy the code below and test it out:

import pytest

from django.test import Client
from library.models import Book

@pytest.mark.django_db
def test_book_model():
    client = Client()
    book = Book.objects.create(
                author = "Charles Dickens",
                title = "Great Expectations")
    expected_value = "Charles Dickens | Great Expectations”
    assert str(book) == expected_value

Over to You!

It’s time to move on to some serious matters! Your next mission involves our Django project. Watch the video below to learn more about the application: 

Because I’m kind, I’m going to give you an initial example of a unit test for this project:

 Your Mission:

  • Write a test plan for this project.

  • Create a sequence of tests for the whole project using pytest-django. 

Find a suggested solution on GitHub!

Let’s Recap!

  • To configure pytest-django, you need to create a  pytest.ini  file containing the path to the project settings file. You must specify the path using the  DJANGO_SETTINGS_MODULE  variable. Otherwise, the tests won’t run.

  • URLs can be tested using the  resolve()  method and the  view_name  attribute.

  • All of the views return the data and an HTTP return code that can be tested in a unit test. The return value for the request provides  content  and  status_code  attributes, which contain the data and the HTTP return code, respectively.

  • Models can be tested using the special method  __str__ , which can be used to send back all information about the model.

We’ve finished looking into the specifics of web applications. Now, we’re going to continue to extend your knowledge of the Pytest framework. I’ll meet you in the next chapter!

Example of certificate of achievement
Example of certificate of achievement