Skip to content

Commit

Permalink
Convert Django test tags to Pytest markers
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bluetech committed Oct 30, 2023
1 parent d8be0ef commit 967618e
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 1 deletion.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 42 additions & 0 deletions pytest_django/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 36 additions & 1 deletion tests/test_unittest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pytest
from django.test import TestCase
from django.test import TestCase, tag

from .helpers import DjangoPytester

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 967618e

Please sign in to comment.