As I was creting some tests for the application I’ve noticed that the creation of users was only being repeated, so I’ve decided to create some sorts of project level Test Class:
@timed is a decorator to get the function runtime
class TestBaseView(TestCase):
app_name: str # Ensures app_name is a defined attribute
def __init__(self, app_name: str, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.app_name = app_name
@classmethod
def setUpTestData(cls) -> None:
"""Set up test data common to all test cases."""
print(f'\n {"="*10} {cls.__name__} {"="*10}')
cls.default_user, cls.manager_user = cls._create_users(cls.get_app_name())
(
cls.unlogged_client,
cls.default_client,
cls.manager_client,
) = Client(), Client(), Client()
cls._log_user_in(cls.default_client, cls.default_user)
cls._log_user_in(cls.manager_client, cls.manager_user)
cls.create_app_specific_objects()
super().setUpTestData()
def setUp(self):
"""Set up for each individual test method."""
super().setUp()
self.temp_data = {}
def tearDown(self):
"""Clean up after each test method."""
self.temp_data.clear()
super().tearDown()
@classmethod
def get_app_name(cls) -> str:
"""
Gets the app name. Needs override, used by other methods.
## Usage:
>>> return "example_app"
"""
raise NotImplementedError(
"Child classes must implement 'get_app_name'."
)
@classmethod
def _create_users(cls, app_name: str) -> Tuple[object, object]:
"""Create and return default and manager user objects."""
return _user_factory(app_name)
@classmethod
def create_app_specific_objects(cls) -> None:
"""Create app-specific objects. Must be implemented in child classes."""
raise NotImplementedError(
"Child classes must implement 'create_app_specific_objects'."
)
@classmethod
def _log_user_in(cls, client: object, user: object) -> None:
client.force_login(user)
and then extending this class in app level:
class TestAppView(TestBaseView):
@classmethod
def setUpTestData(cls):
super().setUpTestData()
# cls.create_app_specific_objects()
@classmethod
def get_app_name(cls):
return "app"
@classmethod
@timed
def create_app_specific_objects(cls):
# some app related object creation
and then the actual test classes:
class TestViewsGET(TestAppView):
@classmethod
def setUpTestData(cls):
super().setUpTestData()
def setUp(self):
print("# setUp ###############################################")
# Initialize clients
super().setUp()
self.rh_client = Client()
self.rh_client.force_login(self.rh_user)
self.rh_manager_client = Client()
self.rh_manager_client.force_login(self.rh_manager_user)
# With methods following test discovery
def test_*(self):
# Test cases here
With all this set, and running $ find . -name “*.pyc” -delete && python manage.py test app.tests.test_views.TestViewsGET --verbosity 3
It “finds” only 1, when theres 4 tests
Found 1 test(s).
System check identified no issues (0 silenced).
========== TestViewsGET ==========
Function '_update_groups' runtime: 0:00:00.189321
Function '_update_groups' runtime: 0:00:15.623892
Function '_user_factory' runtime: 0:00:16.392986
Function 'create_app_specific_objects' runtime: 0:00:01.654744
Traceback (most recent call last):
File "/home/webapp/manage.py", line 22, in <module>
main()
File "/home/webapp/manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "/home/webapp/.venv/lib/python3.11/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
utility.execute()
File "/home/webapp/.venv/lib/python3.11/site-packages/django/core/management/__init__.py", line 436, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/home/webapp/.venv/lib/python3.11/site-packages/django/core/management/commands/test.py", line 24, in run_from_argv
super().run_from_argv(argv)
File "/home/webapp/.venv/lib/python3.11/site-packages/django/core/management/base.py", line 412, in run_from_argv
self.execute(*args, **cmd_options)
File "/home/webapp/.venv/lib/python3.11/site-packages/django/core/management/base.py", line 458, in execute
output = self.handle(*args, **options)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/webapp/.venv/lib/python3.11/site-packages/django/core/management/commands/test.py", line 68, in handle
failures = test_runner.run_tests(test_labels)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/webapp/.venv/lib/python3.11/site-packages/django/test/runner.py", line 1068, in run_tests
result = self.run_suite(suite)
^^^^^^^^^^^^^^^^^^^^^
File "/home/webapp/.venv/lib/python3.11/site-packages/django/test/runner.py", line 995, in run_suite
return runner.run(suite)
^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/unittest/runner.py", line 217, in run
test(result)
File "/usr/lib/python3.11/unittest/suite.py", line 84, in __call__
return self.run(*args, **kwds)
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/unittest/suite.py", line 122, in run
test(result)
File "/home/webapp/.venv/lib/python3.11/site-packages/django/test/testcases.py", line 258, in __call__
self._setup_and_call(result)
File "/home/webapp/.venv/lib/python3.11/site-packages/django/test/testcases.py", line 273, in _setup_and_call
testMethod = getattr(self, self._testMethodName)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'TestViewsGET' object has no attribute 'runTest'. Did you mean: 'subTest'?