Django includes a testing framework built on the standard library's unittest. It creates and destroys a test database for you and provides a test client that sends requests without a browser. If you prefer pytest, the pytest-django plugin gives you the same capabilities.
Running tests
Put tests in an app's tests.py or in files whose names start with test inside a tests/ package. manage.py test discovers and runs them.
# Everything
python manage.py test
# One app, one class, one method
python manage.py test polls
python manage.py test polls.tests.QuestionModelTests
python manage.py test polls.tests.QuestionModelTests.test_str
# Keep the test database between runs
python manage.py test --keepdb
# Split the run across several processes
python manage.py test --parallel
Django creates a separate database, named with a test_ prefix, and applies your migrations to it. Your real data is never touched.
Testing models with TestCase
django.test.TestCase wraps each test method in a transaction and rolls it back afterwards, so data created in one test never leaks into another. Data shared by several tests can be created once per class in setUpTestData, which keeps the suite fast.
# polls/tests.py
from datetime import timedelta
from django.test import TestCase
from django.utils import timezone
from .models import Question
class QuestionModelTests(TestCase):
@classmethod
def setUpTestData(cls):
cls.question = Question.objects.create(question_text="Favorite language?")
def test_str(self):
self.assertEqual(str(self.question), "Favorite language?")
def test_future_question_is_not_listed(self):
Question.objects.create(
question_text="Future question",
pub_date=timezone.now() + timedelta(days=30),
)
listed = Question.objects.filter(pub_date__lte=timezone.now())
self.assertQuerySetEqual(listed, [self.question])
For pure functions that never touch the database, SimpleTestCase runs faster.
Testing views with the test client
self.client acts like a minimal browser that sends GET and POST requests. The response also exposes the templates and context used to render it.
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse
from .models import Question
class QuestionViewTests(TestCase):
def test_index_lists_questions(self):
Question.objects.create(question_text="First question?")
response = self.client.get(reverse("polls:index"))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "First question?")
self.assertTemplateUsed(response, "polls/index.html")
def test_detail_returns_404_for_missing_question(self):
response = self.client.get(reverse("polls:detail", args=[999]))
self.assertEqual(response.status_code, 404)
def test_create_requires_login(self):
url = reverse("polls:create")
response = self.client.post(url, {"question_text": "New question?"})
self.assertEqual(response.status_code, 302)
user = get_user_model().objects.create_user("kim", password="pass1234")
self.client.force_login(user)
response = self.client.post(url, {"question_text": "New question?"})
self.assertEqual(Question.objects.count(), 1)
0 則留言
登入 · 登入後即可留言。