Skip to content

Commit 967618e

Browse files
committed
Convert Django test tags to Pytest markers
Django TestCase supports tags: https://docs.djangoproject.com/en/4.2/topics/testing/tools/#topics-tagging-tests These are basically similar to (basic) Pytest tags, so let's interpret them to allow using the native pytest-native markers functionality. This helps projects which are unable to convert tags to markers. This may cause breakage for projects using `strict-markers`. Such projects would need to add the tags to their `markers` config, or deal with it some other way. Fix #818.
1 parent d8be0ef commit 967618e

File tree

3 files changed

+79
-1
lines changed

3 files changed

+79
-1
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ addopts = [
1818
]
1919
DJANGO_SETTINGS_MODULE = "pytest_django_test.settings_sqlite_file"
2020
testpaths = ["tests"]
21+
markers = ["tag1", "tag2", "tag3", "tag4", "tag5"]
2122

2223
[tool.mypy]
2324
strict = true

pytest_django/plugin.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,48 @@ def pytest_report_header(config: pytest.Config) -> Optional[List[str]]:
377377
return None
378378

379379

380+
# Convert Django test tags on test classes to pytest marks.
381+
def pytest_collectstart(collector: pytest.Collector) -> None:
382+
if "django" not in sys.modules:
383+
return
384+
385+
if not isinstance(collector, pytest.Class):
386+
return
387+
388+
tags = getattr(collector.obj, "tags", ())
389+
if not tags:
390+
return
391+
392+
from django.test import TransactionTestCase
393+
394+
if not issubclass(collector.obj, TransactionTestCase):
395+
return
396+
397+
for tag in tags:
398+
collector.add_marker(tag)
399+
400+
401+
# Convert Django test tags on test methods to pytest marks.
402+
def pytest_itemcollected(item: pytest.Item) -> None:
403+
if "django" not in sys.modules:
404+
return
405+
406+
if not isinstance(item, pytest.Function):
407+
return
408+
409+
tags = getattr(item.obj, "tags", ())
410+
if not tags:
411+
return
412+
413+
from django.test import TransactionTestCase
414+
415+
if not issubclass(item.cls, TransactionTestCase):
416+
return
417+
418+
for tag in tags:
419+
item.add_marker(tag)
420+
421+
380422
@pytest.hookimpl(tryfirst=True)
381423
def pytest_collection_modifyitems(items: List[pytest.Item]) -> None:
382424
# If Django is not configured we don't need to bother

tests/test_unittest.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import pytest
2-
from django.test import TestCase
2+
from django.test import TestCase, tag
33

44
from .helpers import DjangoPytester
55

@@ -57,6 +57,41 @@ def tearDown(self) -> None:
5757
assert Item.objects.count() == 3
5858

5959

60+
@tag("tag1", "tag2")
61+
class TestDjangoTagsToPytestMarkers(TestCase):
62+
"""Django test tags are converted to Pytest markers, at the class & method
63+
levels."""
64+
65+
@pytest.fixture(autouse=True)
66+
def gimme_my_markers(self, request: pytest.FixtureRequest) -> None:
67+
self.markers = {m.name for m in request.node.iter_markers()}
68+
69+
@tag("tag3", "tag4") # type: ignore[misc]
70+
def test_1(self) -> None:
71+
assert self.markers == {"tag1", "tag2", "tag3", "tag4"}
72+
73+
def test_2(self) -> None:
74+
assert self.markers == {"tag1", "tag2"}
75+
76+
@tag("tag5") # type: ignore[misc]
77+
def test_3(self) -> None:
78+
assert self.markers == {"tag1", "tag2", "tag5"}
79+
80+
81+
@tag("tag1")
82+
class TestNonDjangoClassWithTags:
83+
"""Django test tags are only converted to Pytest markers if actually
84+
Django tests. Use pytest markers directly for pytest tests."""
85+
86+
@pytest.fixture(autouse=True)
87+
def gimme_my_markers(self, request: pytest.FixtureRequest) -> None:
88+
self.markers = {m.name for m in request.node.iter_markers()}
89+
90+
@tag("tag2") # type: ignore[misc]
91+
def test_1(self) -> None:
92+
assert not self.markers
93+
94+
6095
def test_sole_test(django_pytester: DjangoPytester) -> None:
6196
"""
6297
Make sure the database is configured when only Django TestCase classes

0 commit comments

Comments
 (0)