New feature testing in Django 3.2


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





assertQuerysetEqual() , !





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.






«Web- Python».





« django». django; : django-debug-toolbar, django-cms, django-cleanup ..








All Articles