Skip to main content

Utils for Django admin testing

Project description

Django-testing-utils

Django-Testing-Utils is a package providing test helpers for django app testing.

build codecov PyPI version

Installation

pip install django-testing-utils

Usage

TestCase metaclass

Django 3.2 introduces setUpTestData attributes isolation, but django-testing-utils has slightly different way of resetting class attributes between tests. It collects all django model objects created in any TestCase class method and runs refresh_from_db() when necessary. It also clears fields_cache for such objects.

from django_testing_utils import mixins
from testapp import models

class SomeTestCase(mixins.BaseTestCase):
    """ Some test case that uses django models."""

    @classmethod
    def setUpTestData(cls):
        super().setUpTestData()
        # In large code django docs recommend to create common objects in 
        # this method to speedup tests setup by reusing objects in db.
        cls.project = models.Project.objects.create(name='project')

    def test_something(self):
        # in this test self.project instance is independent from other tests
        ...

Time mocking

There are multiple ways to freeze time in tests:

  • ad-hoc mocking with unittest.mock
  • freezegun library
  • any system approach that puts working with time in order

django-testing-utils provides a way to use last approach in test with some limitations:

  • Project code must work with django.utils.timezone methods only
  • All tests should inherit TimeMixin from django-testing-utils

This leads to a systematic way of datetime and timezone usage in the project and it's tests.

from django.test import TestCase
from django_testing_utils.mixins import TimeMixin, second


class MyTimeTestCase(TimeMixin, TestCase):
    @classmethod
    def setUpTestData(cls):
        # time is not mocked here yet
        ...

    def setUp(self) -> None:
        # not yet...
        super().setUp()
        # ... and here time has been frozen to `self.now`
    
    def test_something(self):
        # simulate time run
        self.now += second

Helpers for django objects

There are some helper methods to work with django objects

  • update_object - performs an UPDATE on django model in a database. This does not affect field values for an object passed to this method.
  • reload - fetches a new model instance from a database via primary key.
  • assert_object_fields fetches an actual version from a database and compares all fields with values passed as named arguments
from django_testing_utils.mixins import BaseTestCase
from testapp import models

class MyTimeTestCase(BaseTestCase):
    
    def test_something(self):
        obj = models.Project.objects.create()
        # change something in db
        self.update_object(obj, name='new')
        # fetch updated version
        new = self.reload(obj)
        # old object is untouched
        self.assertNotEqual(new.name, obj.name)
        # you could fetch and object and compare some fields in a single call 
        self.assert_object_fields(obj, name='new')

Decorators

  • override_defaults - a test case/test method decorator to change default values that are stored in app.defaults module. The idea of app.defaults is to reduce a number of rarely changed variables in django settings module by moving it to application-specific settings

  • disable_patcher - a context manager / decorator to temporarily disable some unittest.patch instances defined in TestCase instance. This breaks open/close principle but allows postponing tests refactoring when some mocks are too generic.

from django.test import TestCase
from django.utils import timezone
from django_testing_utils.mixins import TimeMixin
from django_testing_utils.utils import override_defaults, disable_patchers
from testapp import defaults
import testapp

class MyTestCase(TimeMixin, TestCase):
    
    @override_defaults(testapp.__name__, setting_1=42)
    def test_setting_value(self):
        self.assertEqual(defaults.setting_1, 42)
        
    @disable_patchers('now_patcher')
    def test_real_time(self):
        # now patcher from TimeMixin is now disabled
        with disable_patchers(self.timezone_datetime_patcher):
          # timezone.datetime patcher is not also disabled
          self.assertNotEqual(timezone.now(), timezone.now())

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-testing-utils-0.4.0.tar.gz (7.9 kB view hashes)

Uploaded source

Built Distribution

Supported by

AWS AWS Cloud computing Datadog Datadog Monitoring Facebook / Instagram Facebook / Instagram PSF Sponsor Fastly Fastly CDN Google Google Object Storage and Download Analytics Huawei Huawei PSF Sponsor Microsoft Microsoft PSF Sponsor NVIDIA NVIDIA PSF Sponsor Pingdom Pingdom Monitoring Salesforce Salesforce PSF Sponsor Sentry Sentry Error logging StatusPage StatusPage Status page