root: Support PyCharm's test runner (#7074)

* Initial commit.

* Use Django's test runner as basis

* Skip already correctly formatted test labels
This commit is contained in:
Philipp Kolberg 2023-10-05 20:13:38 +02:00 committed by GitHub
parent 36c10c74e4
commit 205d3d10e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 53 additions and 8 deletions

View File

@ -1,8 +1,10 @@
"""Integrate ./manage.py test with pytest""" """Integrate ./manage.py test with pytest"""
import os
from argparse import ArgumentParser from argparse import ArgumentParser
from unittest import TestCase from unittest import TestCase
from django.conf import settings from django.conf import settings
from django.test.runner import DiscoverRunner
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
from authentik.lib.sentry import sentry_init from authentik.lib.sentry import sentry_init
@ -12,13 +14,11 @@ from tests.e2e.utils import get_docker_tag
TestCase.maxDiff = None TestCase.maxDiff = None
class PytestTestRunner: # pragma: no cover class PytestTestRunner(DiscoverRunner): # pragma: no cover
"""Runs pytest to discover and run tests.""" """Runs pytest to discover and run tests."""
def __init__(self, verbosity=1, failfast=False, keepdb=False, **kwargs): def __init__(self, verbosity=1, failfast=False, keepdb=False, **kwargs):
self.verbosity = verbosity super().__init__(verbosity, failfast, keepdb, **kwargs)
self.failfast = failfast
self.keepdb = keepdb
self.args = [] self.args = []
if self.failfast: if self.failfast:
@ -47,16 +47,61 @@ class PytestTestRunner: # pragma: no cover
@classmethod @classmethod
def add_arguments(cls, parser: ArgumentParser): def add_arguments(cls, parser: ArgumentParser):
"""Add more pytest-specific arguments""" """Add more pytest-specific arguments"""
parser.add_argument("--randomly-seed", type=int) parser.add_argument(
parser.add_argument("--keepdb", action="store_true") "--randomly-seed",
type=int,
help="Set the seed that pytest-randomly uses (int), or pass the special value 'last'"
"to reuse the seed from the previous run."
"Default behaviour: use random.Random().getrandbits(32), so the seed is"
"different on each run.",
)
parser.add_argument(
"--keepdb", action="store_true", help="Preserves the test DB between runs."
)
def run_tests(self, test_labels): def run_tests(self, test_labels, extra_tests=None, **kwargs):
"""Run pytest and return the exitcode. """Run pytest and return the exitcode.
It translates some of Django's test command option to pytest's. It translates some of Django's test command option to pytest's.
It is supported to only run specific classes and methods using
a dotted module name i.e. foo.bar[.Class[.method]]
The extra_tests argument has been deprecated since Django 5.x
It is kept for compatibility with PyCharm's Django test runner.
""" """
for label in test_labels:
valid_label_found = False
label_as_path = os.path.abspath(label)
# File path has been specified
if os.path.exists(label_as_path):
self.args.append(label_as_path)
valid_label_found = True
else:
# Already correctly formatted test found (file_path::class::method)
if "::" in label:
self.args.append(label)
valid_label_found = True
# Convert dotted module path to file_path::class::method
else:
path_pieces = label.split(".")
# Check whether only class or class and method are specified
for i in range(-1, -3, -1):
path = os.path.join(*path_pieces[:i]) + ".py"
label_as_path = os.path.abspath(path)
if os.path.exists(label_as_path):
path_method = label_as_path + "::" + "::".join(path_pieces[i:])
self.args.append(path_method)
valid_label_found = True
break
if not valid_label_found:
raise RuntimeError(
f"One of the test labels: {label!r}, "
f"is not supported. Use a dotted module name or "
f"path instead."
)
import pytest import pytest
self.args.extend(test_labels)
return pytest.main(self.args) return pytest.main(self.args)