
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
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!
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.settingsYou 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_*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.
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Ā .
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.
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.
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.
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.
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_valueItā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!
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!