import pkgutil from importlib import import_module from django.apps import apps as django_apps from django.test.runner import DiscoverRunner def _local_test_labels(): """core.tests, core.tests_* и apps..tests — без discover('.').""" labels = [] for config in django_apps.get_app_configs(): if not (config.name == 'core' or config.name.startswith('apps.')): continue try: package = import_module(config.name) except ImportError: continue paths = getattr(package, '__path__', None) if not paths: continue for _finder, name, _ispkg in pkgutil.iter_modules(list(paths)): if name == 'tests' or name.startswith('tests_'): labels.append(f'{config.name}.{name}') return labels class AcoreDiscoverRunner(DiscoverRunner): """Без аргументов прогоняет тесты всех локальных приложений. `manage.py test` ходит в discover('.'), а unittest видит только пакеты с `__init__.py` в корне. Приложения лежат в `apps.` без корневого пакета `apps` — их tests.py не попадают в suite. Явные dotted-имена `apps.feed.tests` это обходят и не конфликтуют друг с другом как top-level модуль `tests`. """ def build_suite(self, test_labels=None, **kwargs): if not test_labels: test_labels = _local_test_labels() return super().build_suite(test_labels, **kwargs)