Skip to content

Commit 9fbd67d

Browse files
authored
Class methods can now be discovered as tests (#10552)
Fix #10525
1 parent eca93db commit 9fbd67d

File tree

6 files changed

+24
-8
lines changed

6 files changed

+24
-8
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ Marcin Bachry
225225
Marco Gorelli
226226
Mark Abramowitz
227227
Mark Dickinson
228+
Marko Pacak
228229
Markus Unterwaditzer
229230
Martijn Faassen
230231
Martin Altmayer

changelog/10525.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Test methods decorated with ``@classmethod`` can now be discovered as tests, following the same rules as normal methods. This fills the gap that static methods were discoverable as tests but not class methods.

doc/en/explanation/goodpractices.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ Conventions for Python test discovery
5050
* In those directories, search for ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_.
5151
* From those files, collect test items:
5252

53-
* ``test`` prefixed test functions or methods outside of class
54-
* ``test`` prefixed test functions or methods inside ``Test`` prefixed test classes (without an ``__init__`` method)
53+
* ``test`` prefixed test functions or methods outside of class.
54+
* ``test`` prefixed test functions or methods inside ``Test`` prefixed test classes (without an ``__init__`` method). Methods decorated with ``@staticmethod`` and ``@classmethods`` are also considered.
5555

5656
For examples of how to customize your test discovery :doc:`/example/pythoncollection`.
5757

src/_pytest/python.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,8 +403,8 @@ def classnamefilter(self, name: str) -> bool:
403403

404404
def istestfunction(self, obj: object, name: str) -> bool:
405405
if self.funcnamefilter(name) or self.isnosetest(obj):
406-
if isinstance(obj, staticmethod):
407-
# staticmethods need to be unwrapped.
406+
if isinstance(obj, (staticmethod, classmethod)):
407+
# staticmethods and classmethods need to be unwrapped.
408408
obj = safe_getattr(obj, "__func__", False)
409409
return callable(obj) and fixtures.getfixturemarker(obj) is None
410410
else:

testing/python/integration.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -416,14 +416,14 @@ def test_class(cls): pass
416416
def test_static(): pass
417417
"""
418418
)
419-
assert len(items) == 3
419+
assert len(items) == 4
420420
assert isinstance(items[0], Function)
421421
assert items[0].name == "test_func"
422422
assert items[0].instance is None
423423
assert isinstance(items[1], Function)
424424
assert items[1].name == "test_method"
425425
assert items[1].instance is not None
426426
assert items[1].instance.__class__.__name__ == "TestIt"
427-
assert isinstance(items[2], Function)
428-
assert items[2].name == "test_static"
429-
assert items[2].instance is None
427+
assert isinstance(items[3], Function)
428+
assert items[3].name == "test_static"
429+
assert items[3].instance is None

testing/test_collection.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,20 @@ def testmethod_two(self, arg0):
735735
assert s.endswith("test_example_items1.testone")
736736
print(s)
737737

738+
def test_classmethod_is_discovered(self, pytester: Pytester) -> None:
739+
"""Test that classmethods are discovered"""
740+
p = pytester.makepyfile(
741+
"""
742+
class TestCase:
743+
@classmethod
744+
def test_classmethod(cls) -> None:
745+
pass
746+
"""
747+
)
748+
items, reprec = pytester.inline_genitems(p)
749+
ids = [x.getmodpath() for x in items] # type: ignore[attr-defined]
750+
assert ids == ["TestCase.test_classmethod"]
751+
738752
def test_class_and_functions_discovery_using_glob(self, pytester: Pytester) -> None:
739753
"""Test that Python_classes and Python_functions config options work
740754
as prefixes and glob-like patterns (#600)."""

0 commit comments

Comments
 (0)