Lançado · em melhoria
Guia de Django · 5/6
Por enquanto, este capítulo está disponível apenas em inglês.
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.
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 --parallelDjango creates a separate database, named with a test_ prefix, and applies your migrations to it. Your real data is never touched.
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.
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)force_login() logs a user in without checking a password. Build URLs with reverse() rather than hard-coded strings so tests survive URL changes.
Use override_settings to change settings for one test or class, and unittest.mock to replace things that must not really run, such as external API calls.
from unittest import mock
from django.core import mail
from django.test import TestCase, override_settings
@override_settings(EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend")
class NotifyTests(TestCase):
def test_sends_mail(self):
mail.send_mail("Subject", "Body", "from@example.com", ["to@example.com"])
self.assertEqual(len(mail.outbox), 1)
@mock.patch("polls.services.fetch_weather", return_value={"temp": 20})
def test_uses_weather(self, fetch):
from polls.services import summary
self.assertIn("20", summary())
fetch.assert_called_once()Django uses the in-memory email backend during tests anyway; the override above just makes it explicit.
If you like pytest's plain assert statements and fixtures, install pytest-django.
python -m pip install pytest pytest-django# pyproject.toml
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "mysite.settings"
python_files = ["tests.py", "test_*.py"]# polls/test_views.py
import pytest
from django.urls import reverse
from polls.models import Question
@pytest.mark.django_db
def test_index(client):
Question.objects.create(question_text="A pytest question?")
response = client.get(reverse("polls:index"))
assert response.status_code == 200
assert "A pytest question?" in response.content.decode()
@pytest.mark.django_db
def test_admin_index(admin_client):
response = admin_client.get("/admin/")
assert response.status_code == 200Tests that touch the database need @pytest.mark.django_db. Fixtures such as client, admin_client, rf (a RequestFactory) and settings are built in, and existing TestCase classes still run. Measure coverage with coverage run -m pytest or pytest-cov.
python manage.py test runs your suite against a separate test database.TestCase rolls back each test's transaction, and setUpTestData creates shared data once.self.client and reverse() let you check views request by request.@pytest.mark.django_db and the client fixture, in less code.
0 comentários
Fazer login · Faça login para deixar um comentário.
Seja o primeiro a comentar.