How to Spread Django Unit Tests Over Multiple Files?
Last Updated :
23 Aug, 2024
Django is a robust backend framework that seamlessly integrates with the "unit test" module. As the Django project grows, the complexity of the tests also increases. It is essential to organize unit tests across multiple files to keep the test suite manageable. This maintains the code readability and helps in easier debugging. In this article, we will learn to separate our tests for views, models, and forms in separate files.
Spread Django Unit Tests Over Multiple Files
Let's set up the project:
django-admin startproject geekproject
cd geekproject
python manage.py startapp geekapp
Add the geekapp to the installed app in geekproject/settings.py file
Python
# ...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'geekapp',
]
# ...
Now, let's create a model, a view and a form.
Creating a Model
In the geekapp/models.py, we can create a Book model.
Python
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=100)
published_date = models.DateField(null=True, blank=True)
def __str__(self):
return self.title
Migrate the database
Appling migrations to create the necessary database tables for the model.
python manage.py makemigrations
python manage.py migrate
Creating a Form
In the geekapp/forms.py, create a form for the Book model.
Python
from django import forms
from .models import Book
class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = ['title', 'author', 'published_date']
Creating a View
In the geekapp/views.py, create a view that uses the Book form.
Python
from django.shortcuts import render, redirect
from .forms import BookForm
def create_book(request):
if request.method == 'POST':
form = BookForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'geekapp/create_book.html', {'form': form})
else:
form = BookForm()
return render(request, 'geekapp/create_book.html', {'form': form})
Creating a Template
In the geekapp/templates/geekapp/create_book.html create a simple template to render the form:
HTML
<!DOCTYPE html>
<html>
<head>
<title>Create Book</title>
</head>
<body>
<h1>Create a new book</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Save</button>
</form>
</body>
</html>
Add the URL Patterns
In the geekapp/urls.py, add a URL pattern for the views.
Python
from django.urls import path
from .views import create_book
urlpatterns = [
path('create/', create_book, name='create_book'),
]
Also include this URL pattern in the project in "main urls.py". To do this use the following code.
Python
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('geekapp.urls')),
]
Write the tests
The django tests are located in tests.py within the app directory. When the test cases grow a single file is not suitable. So, the need for tests into multiple files arises. To structure the extended test files. Create a "tests_suite" directory inside the Django app. Organize the test cases by functionality inside the "tests_suite" directory and create separate files for different aspects based on the application. For example,
- "test_models.py" is used for testing models
- "test_views.py" is used for testing views
- "test_forms.py" is used for testing forms
Include the "__init__.py" file to make the tests, create an empty "__init__.py" file inside the tests directory. This allows Django to run all the tests within the directory. The directory structure is in the following format.
Running Tests from Multiple Files
Testing the Models:
In the geekapp/tests_suite/test_models.py, write tests for the Book model.
Python
from django.test import TestCase
from geekapp.models import Book
class BookModelTest(TestCase):
def setUp(self):
self.book = Book.objects.create(title='Test Book',
author='Test Author', published_date='2023-01-01')
def test_book_creation(self):
self.assertEqual(self.book.title, 'Test Book')
self.assertEqual(self.book.author, 'Test Author')
self.assertEqual(self.book.published_date, '2023-01-01')
Testing the Views
In the geekapp/tests_suite/tests_views.py, write tests for the views.
Python
from django.test import TestCase
from django.urls import reverse
from geekapp.models import Book
from geekapp.forms import BookForm
class CreateBookViewTests(TestCase):
def setUp(self):
self.url = reverse('create_book')
def test_create_book_view_get(self):
"""
Test that the create_book view renders the form on a GET request.
"""
response = self.client.get(self.url)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'geekapp/create_book.html')
self.assertIsInstance(response.context['form'], BookForm)
def test_create_book_view_post_valid_data(self):
"""
Test that the create_book view saves the book and redirects on a valid POST request.
"""
valid_data = {
'title': 'Test Book',
'author': 'Test Author',
'published_date': '2024-08-23',
'isbn': '1234567890123',
'price': 19.99,
}
response = self.client.post(self.url, valid_data)
self.assertEqual(Book.objects.count(), 1)
book = Book.objects.first()
self.assertEqual(book.title, 'Test Book')
self.assertEqual(book.author, 'Test Author')
def test_create_book_view_post_invalid_data(self):
"""
Test that the create_book view re-renders the form with errors on an invalid POST request.
"""
invalid_data = {
'title': '', # Title is required, so this should fail
'author': 'Test Author',
'published_date': '2024-08-23',
'isbn': '1234567890123',
'price': 19.99,
}
response = self.client.post(self.url, invalid_data)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'geekapp/create_book.html')
self.assertIsInstance(response.context['form'], BookForm)
self.assertFalse(response.context['form'].is_valid())
self.assertEqual(Book.objects.count(), 0) # No book should be created
Testing the Forms
In the geekapp/tests_suite/tests_forms.py, write tests for the form.
Python
from django.test import TestCase
from .forms import BookForm
class BookFormTest(TestCase):
def test_valid_form(self):
form = BookForm(data={
'title': 'Test Book', 'author': 'Test Author', 'published_date': '2023-01-01'})
self.assertTrue(form.is_valid())
def test_invalid_form(self):
form = BookForm(data={'title': '', 'author': '', 'published_date': ''})
self.assertFalse(form.is_valid())
Run the tests
When the user starts to run the tests, Django's default behavior is to find all the test cases (i.e. subclasses of unittest.TestCase) in any of the file whose name starts with test, it automatically builds the test suite for those test case classes .Then it runs that suite. The default startapp template creates a tests.py file for the new application, which works well for a few tests. However, as your test suite expands, you’ll probably want to organize it into a tests package, allowing you to split tests into separate modules like test_models.py, test_views.py and test_forms.py
Django automatically detects the pattern "test*.py". When the Django test command starts to run, it picks up all the files in this directory. To run the Django project use the command.
To Run all the test cases at once:
python manage.py test
python manage.py test
To run tests from a single file in tests_models.py
python manage.py test geekapp.tests_suite.tests_models
python manage.py test geekapp.tests_suite.tests_models
To run a specific test class from a test file, MyModelTestCase from test_models.py
python manage.py test geekapp.tests_suite.tests_models.BookModelTest
python manage.py test geekapp.tests_suite.tests_models.BookModelTest
To run a specific test method from a class, test_model_creation from MyModelTestCase.
python manage.py test myapp.tests_suite.tests_models.BookModelTest.test_model_creation
python manage.py test myapp.tests_suite.tests_models.BookModelTest.test_model_creation
Conclusion
Organizing the test cases in Django across multiple files is a best practice for maintenance of clean and scalable code. By creating a "tests" directory and dividing them into separate files based on their functionalities make the test suite more modular and easier to manage. Django built in test_discovery helps to run all the test cases regardless of how they are organized in the directory structure.
1. What are Django test cases?
Django test cases are a way to ensure that your Django application behaves as expected. They are written using Python’s unittest framework and Django’s test utilities. Test cases can cover various aspects of your application, such as views, models and forms
2. How do I handle test databases in Django?
Django automatically sets up a separate test database for running tests. This ensures that your test cases do not affect your production or development databases. If you don't need to manually configure this; Django handles it when you run python "manage.py test".
3. How can I ensure that my tests are running in a clean state?
Django's TestCase class uses transactions to ensure that the database is rolled back to its previous state after each test method. This means that your tests will not interfere with each other as long as they are using the TestCase class.
Similar Reads
How to Skip a Unit Test in Django
Django has an excellent built-in testing framework that allows us to test various parts of our application, including models, views, forms, and more."Wait, aren't we supposed to run all our tests?" well, usually yes, however, there are times when we may want to skip a specific test temporarily becau
6 min read
How to Import a JSON File to a Django Model?
In many web development projects, it's common to encounter the need to import data from external sources into a Django application's database. This could be data obtained from APIs, CSV files, or in this case, JSON files. JSON (JavaScript Object Notation) is a popular format for structuring data, an
3 min read
How to combine multiple QuerySets in Django?
QuerySets allow you to filter, order, and manipulate data from your database using a high-level Pythonic syntax. However, there are situations where you may need to combine multiple QuerySets into a single QuerySet to work with the data more efficiently. This article will explore various methods to
5 min read
How to Pull a Random Record Using Django's ORM?
Django's Object-Relational Mapping (ORM) is a powerful tool that allows developers to interact with the database using Python code instead of raw SQL queries. One common task in web development is to pull a random record from the database, whether for displaying a random quote, product, or any other
3 min read
How to run multiple test classes in TestNG?
TestNG makes it easy to run multiple test classes in one go, which helps test different parts of an application efficiently. By running multiple classes together, you save time and ensure more thorough testing. You can run these classes using a TestNG XML file, directly from Eclipse, or through the
2 min read
How To Add Unit Testing to Django Project
Unit testing is the practice of testing individual components or units of code in isolation to ensure they function correctly. In the context of Django, units typically refer to functions, methods, or classes within your project. Unit tests are crucial for detecting and preventing bugs early in deve
4 min read
How to Register Users in Django REST Framework?
Django REST Framework (DRF) is a powerful and flexible toolkit for building Web APIs. One of the most common requirements in web applications is user registration. In this article, we'll explore how to implement user registration using Django REST Framework.Setting Up Django REST FrameworkBefore we
4 min read
How to Add Multiple Arguments to Custom Template Filter in a Django Template
In Django, template filters are useful for modifying data before displaying it in templates. While built-in filters cover common cases, custom logic is sometimes necessary, particularly when handling multiple arguments.Django only allows one argument to our filter. But we can use different technique
4 min read
How to run multiple Python file in a folder one after another?
In this article, we will discuss how to run multiple python files in a folder one after another. There can be many ways to this task, here, we will discuss a few of them. For doing this program, we have to create some python files in one folder and give some names to that folder. The content inside
3 min read
Handle Multiple Forms on a Single Page in Django
One common scenario in web development is handling multiple forms on a single page. This might be required when a user needs to fill out various types of information without navigating away from the page. In this article, we will explore the best practices for managing multiple forms on a single pag
6 min read