Automated Testing & TDD
Prove your app works with Django’s TestCase and the test client — and learn the test-first (TDD) workflow paid courses are built around.
What you will learn
- Write model and view tests with TestCase
- Use the test client to request pages
- Follow the red–green–refactor TDD cycle
Why tests are a hiring filter
A test is code that checks your other code does what you expect — automatically. Instead of clicking through the site by hand after every change, you run one command and the computer verifies everything still works. Backend teams treat tests as non-negotiable: the best-selling Django courses are built entirely around testing, and "do you write tests?" is a common interview gate. The good news: Django ships testing in the box.
Your first test with TestCase
Django’s TestCase is a class you subclass; each method whose name starts with test_ is one test. The magic word is assert — a statement that says "this MUST be true; fail the test if not." Django also gives every test a fresh, empty database so tests never touch your real data.
# blog/tests.py
from django.test import TestCase
from .models import Post
class PostModelTest(TestCase):
def test_str_returns_title(self):
post = Post.objects.create(title="Hello", body="Hi")
# the model's __str__ should return its title:
self.assertEqual(str(post), "Hello")Reading it: PostModelTest groups related tests. Inside, test_str_returns_title creates a post in the throwaway test database, then self.assertEqual(str(post), "Hello") checks the two values match. If they differ, the test fails and tells you exactly where.
Testing a view with the test client
The test client is a fake browser built into TestCase as self.client. It lets you request your own pages in a test and inspect the response — no real server needed. You check the status code (200 means OK, 404 not found) and that the right content appears:
from django.test import TestCase
from django.urls import reverse
from .models import Post
class PostListViewTest(TestCase):
def test_list_shows_posts(self):
Post.objects.create(title="Hello", body="Hi")
# ask for the page using its URL name:
response = self.client.get(reverse("post_list"))
self.assertEqual(response.status_code, 200) # page loaded
self.assertContains(response, "Hello") # title is on the pageStep by step: we create a post, then self.client.get(...) requests the list page. assertEqual(response.status_code, 200) confirms it loaded without error, and assertContains(response, "Hello") confirms our post’s title actually shows up in the HTML.
Running the tests
One command finds every test_ method in your project and runs them all:
python manage.py testNote: Output:
Creating test database for alias 'default'...
..
----------------------------------------------------------------------
Ran 2 tests in 0.012s
OK
Each dot is a passing test; OK at the end means all passed. A failure shows an F and prints exactly which assertion broke.
TDD: write the test first
Test-Driven Development (TDD) flips the order: you write the test before the code. It sounds backwards but keeps you focused and guarantees coverage. The cycle is three steps, repeated:
- Red — write a test for a feature that does not exist yet, and run it. It fails (red) because the code is not there.
- Green — write the smallest amount of code to make that test pass (green). Nothing fancy.
- Refactor — clean up the code now that the test protects you. Run the tests again; still green means you did not break anything.
Note: Concrete example: you want posts to default to "draft" status. Red: write self.assertEqual(post.status, "draft") — it fails, there is no status field. Green: add status = models.CharField(default="draft") and migrate — the test passes. Refactor: tidy up, re-run, still green. You now have a tested feature.
Note: Many teams use pytest with the pytest-django plugin instead of the built-in runner — shorter test code (plain assert instead of self.assertEqual) and nicer output. Install with pip install pytest-django, then run pytest. The ideas (test the model, test the view, TDD) are identical.
Tip: A practical habit: every time you find a bug, first write a failing test that reproduces it, then fix the code until the test passes. The bug can never silently come back.
Q. What is the correct order of the TDD “red–green–refactor” cycle?
✍️ Practice
- Write a
TestCasethat checks your model’s__str__returns the right value. - Use the test client to request your list page and assert it returns status 200 and contains a record’s title.
🏠 Homework
- Pick one feature and build it TDD-style: write the failing test first, then the code, then refactor — and run
python manage.py testuntil green.