tests.py — Proving It Works
You changed the recipe URLs last week. Did the search page break? The detail page? The only honest way to know, every time, is to have the computer check. Django's test tools can pretend to be a browser, visit your pages and verify the answers in seconds.
The rule
Arrange data, send a fake request, assert on the response.
# recipes/tests.py
from django.test import TestCase
from django.urls import reverse
from .models import Recipe
class RecipeDetailTests(TestCase):
def setUp(self):
Recipe.objects.create(title="Tomato soup", slug="tomato-soup", minutes=25)
def test_shows_recipe(self):
url = reverse("recipes:detail", args=["tomato-soup"])
response = self.client.get(url)
self.assertContains(response, "Tomato soup")
def test_missing_recipe_is_404(self):
url = reverse("recipes:detail", args=["no-such-dish"])
response = self.client.get(url)
self.assertEqual(response.status_code, 404)The parts:
TestCasefromdjango.test, not plainunittest. It adds the test client and database handling.- Methods whose names start with
testare tests; others, likesetUp, are helpers.setUpruns before each test. self.clientsends requests through the whole stack, middleware, URLs, view and template, without a real server.assertContainschecks the status is 200 and the text appears in the page.reverse()again: tests use names, so they survive URL changes too.
Run them:
python manage.py test recipesYour real data is safe. Django creates a separate test database for the run and destroys it afterwards, and TestCase rolls back every test's changes, so tests never see each other's rows.
What to test first: each page loads, missing objects give 404, and any rule you care about, such as "quick recipes excludes a 45-minute stew".
The shortcut
Every test is three beats: arrange, act, assert. Create just enough data, make one request or call, check one behaviour. If a test needs a paragraph to explain, split it.
Arrange
Create a 25-minute soup and a 45-minute stew.
The trap is a test that never fails. Break the view on purpose once and watch the test go red; then you know it guards something.
Drills
Answer fast — trust the rule.
Which method name will Django run as a test?
Does python manage.py test read and change your real db.sqlite3 recipes?
self.assertContains(response, "Tomato soup") checks which two things?
What does self.client.get(url) do in a test?
In the arrange-act-assert rhythm, which line is "act"?
Remember
- Arrange data, send a fake request, assert on the response.
TestCase,test_names,self.client, andreverse()for URLs.- Trap: a test you have never seen fail may not be testing anything.
