diff --git a/pyproject.toml b/pyproject.toml index 8ab0bc14..ca497ac6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ addopts = [ ] DJANGO_SETTINGS_MODULE = "pytest_django_test.settings_sqlite_file" testpaths = ["tests"] +markers = ["tag1", "tag2", "tag3", "tag4", "tag5"] [tool.mypy] strict = true diff --git a/pytest_django/plugin.py b/pytest_django/plugin.py index dc151a79..21462c23 100644 --- a/pytest_django/plugin.py +++ b/pytest_django/plugin.py @@ -377,6 +377,48 @@ def pytest_report_header(config: pytest.Config) -> Optional[List[str]]: return None +# Convert Django test tags on test classes to pytest marks. +def pytest_collectstart(collector: pytest.Collector) -> None: + if "django" not in sys.modules: + return + + if not isinstance(collector, pytest.Class): + return + + tags = getattr(collector.obj, "tags", ()) + if not tags: + return + + from django.test import TransactionTestCase + + if not issubclass(collector.obj, TransactionTestCase): + return + + for tag in tags: + collector.add_marker(tag) + + +# Convert Django test tags on test methods to pytest marks. +def pytest_itemcollected(item: pytest.Item) -> None: + if "django" not in sys.modules: + return + + if not isinstance(item, pytest.Function): + return + + tags = getattr(item.obj, "tags", ()) + if not tags: + return + + from django.test import TransactionTestCase + + if not issubclass(item.cls, TransactionTestCase): + return + + for tag in tags: + item.add_marker(tag) + + @pytest.hookimpl(tryfirst=True) def pytest_collection_modifyitems(items: List[pytest.Item]) -> None: # If Django is not configured we don't need to bother diff --git a/tests/test_unittest.py b/tests/test_unittest.py index d98597b5..0b51d054 100644 --- a/tests/test_unittest.py +++ b/tests/test_unittest.py @@ -1,5 +1,5 @@ import pytest -from django.test import TestCase +from django.test import TestCase, tag from .helpers import DjangoPytester @@ -57,6 +57,41 @@ def tearDown(self) -> None: assert Item.objects.count() == 3 +@tag("tag1", "tag2") +class TestDjangoTagsToPytestMarkers(TestCase): + """Django test tags are converted to Pytest markers, at the class & method + levels.""" + + @pytest.fixture(autouse=True) + def gimme_my_markers(self, request: pytest.FixtureRequest) -> None: + self.markers = {m.name for m in request.node.iter_markers()} + + @tag("tag3", "tag4") # type: ignore[misc] + def test_1(self) -> None: + assert self.markers == {"tag1", "tag2", "tag3", "tag4"} + + def test_2(self) -> None: + assert self.markers == {"tag1", "tag2"} + + @tag("tag5") # type: ignore[misc] + def test_3(self) -> None: + assert self.markers == {"tag1", "tag2", "tag5"} + + +@tag("tag1") +class TestNonDjangoClassWithTags: + """Django test tags are only converted to Pytest markers if actually + Django tests. Use pytest markers directly for pytest tests.""" + + @pytest.fixture(autouse=True) + def gimme_my_markers(self, request: pytest.FixtureRequest) -> None: + self.markers = {m.name for m in request.node.iter_markers()} + + @tag("tag2") # type: ignore[misc] + def test_1(self) -> None: + assert not self.markers + + def test_sole_test(django_pytester: DjangoPytester) -> None: """ Make sure the database is configured when only Django TestCase classes