Django 3.2 released its first alpha release a couple of weeks ago, with the final release due in April. It contains a mix of new features, which you can read about in the release notes. This article focuses on testing changes, some of which can be obtained from earlier versions of Django with packages backport
.
1. setUpTestData()
:
«, TestCase.setUpTestData()
».
setUpTestData()
— , .
TestCase.setUp()
- , :
from django.test import TestCase
from example.core.models import Book
class ExampleTests(TestCase):
def setUp(self):
self.book = Book.objects.create(title="Meditations")
- setUp()
. , . , setUp()
, .
setUpTestData()
— TestCase
. setUp()
, :
from django.test import TestCase
from example.core.models import Book
class ExampleTests(TestCase):
@classmethod
def setUpTestData(cls):
cls.book = Book.objects.create(title="Meditations")
Django (rollback) , . , Django 3.2 rollback , . , .
, , :
from django.test import TestCase
from example.core.models import Book
class SetUpTestDataTests(TestCase):
@classmethod
def setUpTestData(cls):
cls.book = Book.objects.create(title="Meditations")
def test_that_changes_title(self):
self.book.title = "Antifragile"
def test_that_reads_title_from_db(self):
db_title = Book.objects.get().title
assert db_title == "Meditations"
def test_that_reads_in_memory_title(self):
assert self.book.title == "Meditations"
Django 3.1, :
$ ./manage.py test example.core.tests.test_setuptestdata
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.F.
======================================================================
FAIL: test_that_reads_in_memory_title (example.core.tests.test_setuptestdata.SetUpTestDataTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/.../example/core/tests/test_setuptestdata.py", line 19, in test_that_reads_in_memory_title
assert self.book.title == "Meditations"
AssertionError
----------------------------------------------------------------------
Ran 3 tests in 0.002s
FAILED (failures=1)
Destroying test database for alias 'default'...
, in-memory test_that_changes_title()
. Django 3.2 , in-memory. :
$ ./manage.py test example.core.tests.test_setuptestdata
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
...
----------------------------------------------------------------------
Ran 3 tests in 0.002s
OK
Destroying test database for alias 'default'...
Simon Charette django-testdata, Django. Django django - , @wrap_testdata
setUpTestData()
. , , .
( Django , , ).
2. faulthandler
:
DiscoverRunner faulthandler .
, . Python faulthandler «» , Python. runner Django faulthandler. pytest, .
, faulthandler, C, , . , OS SIGSEGV , , . Python, :
import os
import signal
from django.test import SimpleTestCase
class FaulthandlerTests(SimpleTestCase):
def test_segv(self):
# Directly trigger the segmentation fault
# signal, which normally occurs due to
# unsafe memory access in C
os.kill(os.getpid(), signal.SIGSEGV)
Django 3.1, :
$ ./manage.py test example.core.tests.test_faulthandler
System check identified no issues (0 silenced).
[1] 31127 segmentation fault ./manage.py test
, , .
Django 3.2:
$ ./manage.py test example.core.tests.test_faulthandler
System check identified no issues (0 silenced).
Fatal Python error: Segmentation fault
Current thread 0x000000010ed1bdc0 (most recent call first):
File "/.../example/core/tests/test_faulthandler.py", line 12 in test_segv
File "/.../python3.9/unittest/case.py", line 550 in _callTestMethod
...
File "/.../django/test/runner.py", line 668 in run_suite
...
File "/..././manage.py", line 17 in main
File "/..././manage.py", line 21 in <module>
[1] 31509 segmentation fault ./manage.py test
( )
Faulthandler , Python, .
3. Timing ()
:
DiscoverRunner , .
manage.py test
--timing
, - :
$ ./manage.py test --timing
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
...
----------------------------------------------------------------------
Ran 3 tests in 0.002s
OK
Destroying test database for alias 'default'...
Total database setup took 0.019s
Creating 'default' took 0.019s
Total database teardown took 0.000s
Total run took 0.028s
. Google Summer of Code 2020.
pytest, --durations N
.
- pytest «» (setup time) , , .
4. (callbacks) transaction.on_commit()
:
TestCase.captureOnCommitCallbacks()
(callbacks functions),transaction.on_commit()
. callbacks,TransactionTestCase
.
, , ATOMIC_REQUESTS Django, ( , !). transaction.on_commit() , , . , :
from django.db import transaction
from django.views.decorators.http import require_http_methods
from example.core.models import ContactAttempt
@require_http_methods(("POST",))
def contact(request):
message = request.POST.get('message', '')
attempt = ContactAttempt.objects.create(message=message)
@transaction.on_commit
def send_email():
send_contact_form_email(attempt)
return redirect('/contact/success/')
, , ContactAttempt.
, callback ( ), on_commit()
. Django callback , TestCase
, (rollback) , .
TransactionTestCase
, . , TestCase
, .
( TransactionTestCase
TestCase
.)
Django 3.2 captureOnCommitCallbacks()
, . callbacks . , :
from django.core import mail
from django.test import TestCase
from example.core.models import ContactAttempt
class ContactTests(TestCase):
def test_post(self):
with self.captureOnCommitCallbacks(execute=True) as callbacks:
response = self.client.post(
"/contact/",
{"message": "I like your site"},
)
assert response.status_code == 302
assert response["location"] == "/contact/success/"
assert ContactAttempt.objects.get().message == "I like your site"
assert len(callbacks) == 1
assert len(mail.outbox) == 1
assert mail.outbox[0].subject == "Contact Form"
assert mail.outbox[0].body == "I like your site"
, captureOnCommitCallbacks()
, execute , , « ()» callbacks. HTTP- , , (callback). , !
captureOnCommitCallbacks()
Django, django-capture-on-commit-callbacks.
5. assertQuerysetEqual()
:
TransactionTestCase.assertQuerysetEqual()
Django
Django 3.2, assertQuerysetEqual()
QuerySet . repr()
. , , , repr() strings
:
from django.test import TestCase
from example.core.models import Book
class AssertQuerySetEqualTests(TestCase):
def test_comparison(self):
Book.objects.create(title="Meditations")
Book.objects.create(title="Antifragile")
self.assertQuerysetEqual(
Book.objects.order_by("title"),
["<Book: Antifragile>", "<Book: Meditations>"],
)
, . repr()
.
Django 3.2 QuerySet
, :
from django.test import TestCase
from example.core.models import Book
class AssertQuerySetEqualTests(TestCase):
def test_comparison(self):
book1 = Book.objects.create(title="Meditations")
book2 = Book.objects.create(title="Antifragile")
self.assertQuerysetEqual(
Book.objects.order_by("title"),
[book2, book1],
)
, . Django.
, Django 3.2 backport.
« django». django; : django-debug-toolbar, django-cms, django-cleanup ..