Package: django-testing-utils
Django-testing-utilsDjango-Testing-Utils is a package providing test helpers for django app testing.Installationpipinstalldjango-testing-utilsUsageTestCase metaclassDjango 3.2introducessetUpTestData 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.fromdjango_testing_utilsimportmixinsfromtestappimportmodelsclassSomeTestCase(mixins.BaseTestCase):""" Some test case that uses django models."""@classmethoddefsetUpTestData(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')deftest_something(self):# in this test self.project instance is independent from other tests...Time mockingThere are multiple ways to freeze time in tests:ad-hoc mocking withunittest.mockfreezegunlibraryany system approach that puts working with time in orderdjango-testing-utils provides a way to use last approach in test with some
limitations:Project code must work withdjango.utils.timezonemethods onlyAll tests should inheritTimeMixinfrom django-testing-utilsThis leads to a systematic way of datetime and timezone usage in the project
and it's tests.fromdjango.testimportTestCasefromdjango_testing_utils.mixinsimportTimeMixin,secondclassMyTimeTestCase(TimeMixin,TestCase):@classmethoddefsetUpTestData(cls):# time is not mocked here yet...defsetUp(self)->None:# not yet...super().setUp()# ... and here time has been frozen to `self.now`deftest_something(self):# simulate time runself.now+=secondHelpers for django objectsThere are some helper methods to work with django objectsupdate_object- performs anUPDATEon 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_fieldsfetches an actual version from a database and
compares all fields with values passed as named argumentsfromdjango_testing_utils.mixinsimportBaseTestCasefromtestappimportmodelsclassMyTimeTestCase(BaseTestCase):deftest_something(self):obj=models.Project.objects.create()# change something in dbself.update_object(obj,name='new')# fetch updated versionnew=self.reload(obj)# old object is untouchedself.assertNotEqual(new.name,obj.name)# you could fetch and object and compare some fields in a single callself.assert_object_fields(obj,name='new')Decoratorsoverride_defaults- a test case/test method decorator to change default
values that are stored inapp.defaultsmodule. The idea ofapp.defaultsis to reduce a number of rarely changed variables in django settings module by
moving it to application-specific settingsdisable_patcher- a context manager / decorator to temporarily disable
someunittest.patchinstances defined in TestCase instance. This breaks
open/close principle but allows postponing tests refactoring when some
mocks are too generic.fromdjango.testimportTestCasefromdjango.utilsimporttimezonefromdjango_testing_utils.mixinsimportTimeMixinfromdjango_testing_utils.utilsimportoverride_defaults,disable_patchersfromtestappimportdefaultsimporttestappclassMyTestCase(TimeMixin,TestCase):@override_defaults(testapp.__name__,setting_1=42)deftest_setting_value(self):self.assertEqual(defaults.setting_1,42)@disable_patchers('now_patcher')deftest_real_time(self):# now patcher from TimeMixin is now disabledwithdisable_patchers(self.timezone_datetime_patcher):# timezone.datetime patcher is not also disabledself.assertNotEqual(timezone.now(),timezone.now())