Testing Django Applications
Introduction
Automated tests protect your blog from regressions—broken URLs, invalid forms, permission bugs. Django ships django.test.TestCase, a test Client that simulates HTTP, and a test database that rolls back after each test. This chapter tests models, views, authentication, and forms—the same confidence Flask pytest provides for microframework apps.
Prerequisites
Run the Test Suite
python manage.py test
python manage.py test blog
python manage.py test blog.tests.test_views.PostListTests.test_list_statusVerbose:
python manage.py test -v 2Django creates a test database (often test_<dbname>), runs tests inside transactions, and destroys it when finished.
Project Layout
blog/
├── tests/
│ ├── __init__.py
│ ├── test_models.py
│ ├── test_views.py
│ └── test_forms.pyOr single blog/tests.py for small apps—split files as tests grow.
Model Tests
blog/tests/test_models.py:
Code explanation:
TestCasewraps each test in a transaction and rolls backsetUpruns before every test method- Use
assertRaisesfor integrity errors (SQLite/MySQL behavior may vary—IntegrityErroris precise)
View Tests With Client
blog/tests/test_views.py:
Code explanation:
reverse("post-list")uses named URLs — routing chapterassertContainschecks rendered HTML
Authentication Tests
client.login() sets session—no need to POST login form in every test.
Form Validation Tests
blog/tests/test_forms.py:
Test forms without HTTP when validation logic is complex.
Fixtures and setUpTestData
For expensive setup shared across tests in a class:
class PostListTests(TestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create_user(username="dave", password="pass12345")
cls.post = Post.objects.create(
title="Shared",
slug="shared",
body="Body",
author=cls.user,
published=True,
)setUpTestData runs once per class—faster than recreating rows every test.
Load JSON fixtures:
python manage.py loaddata blog/fixtures/posts.jsonUse sparingly in tests—prefer setUp factories for clarity.
pytest-django (Optional)
Many teams use pytest:
pip install pytest pytest-djangopytest.ini:
[pytest]
DJANGO_SETTINGS_MODULE = config.settings.development
python_files = tests.py test_*.pyblog/tests/test_views_pytest.py:
import pytest
@pytest.mark.django_db
def test_post_list(client, django_user_model):
user = django_user_model.objects.create_user("eve", password="pass12345")
...Same assertions—pytest style is preference, not required for this track.
CI Hook (Preview)
Run tests in GitHub Actions — full example in Docker and CI:
- run: python manage.py testPost-Chapter Checklist
-
python manage.py testpasses for your app - Model
__str__and constraints tested - Views tested for status codes and content
- Login-required views tested with
client.login - At least one form validation test
FAQ
Tests use wrong database?
Check DATABASES in test settings—Django clones default DB.
CSRF failures in tests?
Client handles CSRF automatically on POST when using follow=True or enforce_csrf_checks=False on client (advanced).
Speed up suite?
--parallel, setUpTestData, mock slow external APIs.
factory_boy?
Generates fake User / Post rows—nice for large suites.
Test migrations?
MigrationTestCase or migrate in integration tests—usually smoke-test migrate in CI instead.