Skip to main content

How to Unit Test in Django

Django's test framework extends unittest.TestCase with django.test.TestCase, providing additional features like:

  • Database Rollback: Each test runs in isolation with a separate test database.
  • Client Simulation: You can simulate requests to views using self.client.
  • Fixture Loading: You can load test data using fixtures.

Step 1 - Set up the Django project

  • Install the necessary packages
pip install django djangorestframework
  • Create a Django Project & App
django-admin startproject myproject
cd myproject
django-admin startapp products
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', # DRF
'products', # Products app
]
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('products.urls')),
]

Step 2 - Create basic home page

def home(request):
return HttpResponse("Hello, Django!")
path('', home, name='home'),
  • Start the application
python manage.py runserver
  • With a web browser navigate to http://127.0.0.1:8000/api/ and confirm the page displays the text from the home function Hello, Django!.

alt text

Step 3 - Unit test basic home page

To unit test the basic home page.

from django.test import TestCase
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from products.models import Product

class ProductAPITestCase(APITestCase):

def test_get_products(self):
"""Test confirms that the API home page display the text 'Hello, Django!'."""

# Arrange
home_url = reverse('home')
expected_response = "Hello, Django!"

# Act
actual_response = self.client.get(home_url)

# Assert
self.assertEqual(actual_response.status_code, status.HTTP_200_OK) # Check the status code to confirm the request was executed successfully
self.assertEqual(actual_response.content.decode(), expected_response) # Check the response content to confirm the expected text is displayed
  • Execute the test unit
python manage.py test products

Step 4 - Create the API

  • Define the Product Model in products/models.py. The model will store the data for the products, such as name and price.
from django.db import models

class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)

def __str__(self):
return self.name
  • Apply migrations:
python manage.py makemigrations
python manage.py migrate
from rest_framework import serializers
from .models import Product

class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
from rest_framework import viewsets
from .models import Product
from .serializers import ProductSerializer
from django.http import HttpResponse

class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer

def home(request):
return HttpResponse("Hello, Django!")
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ProductViewSet
from .views import home

router = DefaultRouter()
router.register(r'products', ProductViewSet)

urlpatterns = [
path('', home, name='home'),
path('', include(router.urls)),
]

Step 5 - Unit test the model

from django.test import TestCase
from products.models import Product

class ProductModelTest(TestCase):

def test_create_product(self):
"""Test confirms that a Product instance is created correctly."""

# Arrange
name = "Laptop"
price = 1000.00

# Act
product = Product.objects.create(name=name, price=price)

# Assert
self.assertEqual(product.name, "Laptop")
self.assertEqual(product.price, 1000.00)
  • Run the unit test.
python manage.py test products

alt text

Step 6 - Unit test the view

  • The following section will explain how to unit test and manually test the product view, using web browser and curl.

  • Run the site by executing python manage.py runserver, so that you can manually test the API using cURL, Postman, or your browser.

alt text

Test Retrieving All Products (GET /api/products/)

alt text

  • To view the product list using curl execute a curl command from the CMD command prompt (PowerShell aliases curl to Invoke-WebRequest, which does not support the -X parameter). In VS Code, create the terminal by clicking the dropdown next to the plus icon, then select Command Prompt.

alt text

curl -X GET http://127.0.0.1:8000/api/products/ 

alt text

from django.test import TestCase
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from products.models import Product

class ProductAPITestCase(APITestCase):

def test_get_products(self):
"""Test confirms that the API returns a list of all products."""

# Arrange - No additional setup needed

# Act
response = self.client.get(self.list_url)

# Assert
self.assertEqual(response.status_code, status.HTTP_200_OK) # Check the status code to confirm the request was executed successfully
  • Execute the tests
python manage.py test products

alt text

Test Creating a Product (POST /api/products/)

In the example we going to add a new product called Phone with the price 500.00. Execute a POST using curl, the response should return 201 Created. Then execute a second curl to get the list of products.

curl -X POST http://127.0.0.1:8000/api/products/ -H "Content-Type: application/json" -d "{\"name\": \"Phone\", \"price\": 500.00}"
curl -X GET http://127.0.0.1:8000/api/products/

alt text

You can also browse http://127.0.0.1:8000/api/products/ to confirm the product was created.

alt text

To unit test, adding products, add the function test_create_product to products/product_tests/test_view.py

    def test_create_product(self):
"""Test confirms that a new product can be created via API."""

# Arrange
data = {"name": "Phone", "price": 500.00}

# Act
response = self.client.post(self.list_url, data)

# Assert
self.assertEqual(response.status_code, status.HTTP_201_CREATED) # Check the status code to confirm a record was created
self.assertEqual(Product.objects.count(), 2) # Confirm the products list contains to items
  • Run the tests
python manage.py test products

alt text

Test Retrieving a Single Product (GET /api/products/1/)

  • To get a single products details, in this example we are going to get the details for the product, with product id 1. Execute curl to get the product details.
curl -X GET http://127.0.0.1:8000/api/products/1/

alt text

alt text

To unit test, adding products, add the function test_get_product_detail

def test_get_product_detail(self):
"""Test confirms that an individual product's details can be retrieved."""

# Arrange - Product is already created in setUp()

# Act
response = self.client.get(self.detail_url)

# Assert
self.assertEqual(response.status_code, status.HTTP_200_OK) # Check the status code to confirm the request was executed successfully
self.assertEqual(response.data['name'], "Laptop") # Check the products name
  • Run the tests
python manage.py test products

alt text

Test Updating a Product (PUT /api/products/1/)

In this example, we are going to update the details for the product with the id 1. We going to change the name to Smartphone and the price to 600.00. Execute the PUT request using curl. The execute a second GET request for product 1 to confirm the product details have been updated.

curl -X PUT http://127.0.0.1:8000/api/products/1/ -H "Content-Type: application/json" -d "{\"name\": \"Smartphone\", \"price\": 600.00}"
curl -X GET http://127.0.0.1:8000/api/products/1/

alt text

  • To unit test updating a product add the function
def test_update_product(self):
"""Test confirms that an existing product can be updated."""

# Arrange
data = {"name": "Smartphone", "price": 600.00}

# Act
response = self.client.put(self.detail_url, data)

# Assert
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.product.refresh_from_db()
self.assertEqual(self.product.name, "Smartphone") # Confirm name change
self.assertEqual(self.product.price, 600.00) # Confirm price change
  • Run the tests
python manage.py test products

alt text

Test Deleting a Product (DELETE /api/products/1/)

  • To delete product id 1, execute DELETE request using curl, then execute a second curl request to confirm the product has been deleted.
curl -X DELETE http://127.0.0.1:8000/api/products/1/
curl -X GET http://127.0.0.1:8000/api/products/1/

alt text

To unit test deleting a product create the function def test_delete_product(self):

def test_delete_product(self):
"""Test confirms that a product can be deleted via API."""

# Arrange - Product exists (created by setup)

# Act
response = self.client.delete(self.detail_url)

# Assert
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(Product.objects.count(), 0)
  • Run the tests
python manage.py test products

alt text

Adverts