Coverage for website/thaliawebsite/tests/test_models.py: 100.00%
24 statements
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
1from django.apps import apps
2from django.core.exceptions import ObjectDoesNotExist
3from django.db import models
4from django.test import TestCase
6# Third-party apps whose models haven't implemented __str__ overrides.
7_EXCLUDED_APPS = {
8 "django_drf_filepond",
9 "thumbnails",
10 "otp_static",
11 "otp_totp",
12}
15def create_models_test_class(classname):
16 """Create the class for all the model __str__ tests.
18 This class is created dynamically with the type(name, bases, dict)
19 function, it includes test functions to test all the models in the project.
21 :param classname: The name to use for the created class
22 :return: An instance of the TestCase class with generated tests
23 """
25 def create_model_test_function(name, test_model):
26 """Create a test function that tests database model test_model."""
28 def str_function_is_overwritten_for(self):
29 """Check if the test_model overrides __str__.
31 Compares the implementation to the super class version.
32 """
33 instance = test_model()
34 try:
35 # the implemented __str__ method should be different from the
36 # __str__ function in the parent class (Model)
37 self.assertNotEqual(str(instance), models.Model.__str__(instance))
38 except (ObjectDoesNotExist, AttributeError, KeyError, TypeError):
39 # if the __str__ method relies on any fields which were not
40 # instantiated, it throws a derivative of ObjectDoesNotExist,
41 # or one of the other errors which means it is different from
42 # the parent class implementation
43 pass
45 # the testing framework uses qualname to print the method name and
46 # its class
47 str_function_is_overwritten_for.__qualname__ = f"{classname}.{name}"
48 str_function_is_overwritten_for.__name__ = name
49 return str_function_is_overwritten_for
51 tests = {}
52 # django keeps track of the models it knows of, and we can request that
53 # here by default these are only the models implemented by the project
54 for model in apps.get_models():
55 if model._meta.app_label in _EXCLUDED_APPS:
56 continue
57 funcname = f"test_str_method_overwritten_for_{model.__name__}"
58 tests[funcname] = create_model_test_function(funcname, model)
60 # type() is the class constructor, it's arguments are
61 # name: name of the class
62 # bases: classes the new class inherits from
63 # dict: attributes (which includes methods) of this class
64 return type(classname, (TestCase,), tests)
67# create the class to be picked up by the django test runner
68ModelsTest = create_models_test_class("ModelsTest")