Skip to main content

Testing utils for Django-based projects

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 testproject.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())

Project details


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.8.0.tar.gz (9.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_testing_utils-0.8.0-py3-none-any.whl (8.4 kB view details)

Uploaded Python 3

File details

Details for the file django_testing_utils-0.8.0.tar.gz.

File metadata

  • Download URL: django_testing_utils-0.8.0.tar.gz
  • Upload date:
  • Size: 9.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for django_testing_utils-0.8.0.tar.gz
Algorithm Hash digest
SHA256 8a7df7d17dd8562617592c4233405c80e90b994ee74bba2a963c45ca2707a32d
MD5 f2cb1b1d53e126aa7232dce08be31abd
BLAKE2b-256 c79a0eac568ea49b5ff9a65736afd541ddd56d1f5818c88492e0284ee671cf02

See more details on using hashes here.

File details

Details for the file django_testing_utils-0.8.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_testing_utils-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 144706a5b6af1fda5d6011568bb54b5709d4d75857afe5b9e299ac69d8a2769b
MD5 174ff8fc07e9ee78b51b3c67990ab048
BLAKE2b-256 3985363e79d5f9e8f9717049395db0b7ad59ee50035b0649c0788a227a8cb197

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page