diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 90f035e8f..513214e08 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -283,6 +283,16 @@ class PyobjMixin(nodes.Node): node = self.getparent(Class) return node.obj if node is not None else None + @property + def instance(self): + """Python instance object the function is bound to. + + Returns None if not a test method, e.g. for a standalone test function, + a staticmethod, a class or a module. + """ + node = self.getparent(Function) + return getattr(node.obj, "__self__", None) if node is not None else None + @property def obj(self): """Underlying Python object.""" @@ -1693,15 +1703,6 @@ class Function(PyobjMixin, nodes.Item): """Underlying python 'function' object.""" return getimfunc(self.obj) - @property - def instance(self): - """Python instance object the function is bound to. - - Returns None if not a test method, e.g. for a standalone test function - or a staticmethod. - """ - return getattr(self.obj, "__self__", None) - def _getobj(self): assert self.parent is not None if isinstance(self.parent, Class): diff --git a/testing/test_collection.py b/testing/test_collection.py index 6fd9a708b..6a8a5c1ce 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -64,7 +64,7 @@ class TestCollector: assert pytester.collect_by_name(modcol, "doesnotexist") is None - def test_getparent(self, pytester: Pytester) -> None: + def test_getparent_and_accessors(self, pytester: Pytester) -> None: modcol = pytester.getmodulecol( """ class TestClass: @@ -77,14 +77,21 @@ class TestCollector: fn = pytester.collect_by_name(cls, "test_foo") assert isinstance(fn, pytest.Function) - module_parent = fn.getparent(pytest.Module) - assert module_parent is modcol + assert fn.getparent(pytest.Module) is modcol + assert modcol.module is not None + assert modcol.cls is None + assert modcol.instance is None - function_parent = fn.getparent(pytest.Function) - assert function_parent is fn + assert fn.getparent(pytest.Class) is cls + assert cls.module is not None + assert cls.cls is not None + assert cls.instance is None - class_parent = fn.getparent(pytest.Class) - assert class_parent is cls + assert fn.getparent(pytest.Function) is fn + assert fn.module is not None + assert fn.cls is not None + assert fn.instance is not None + assert fn.function is not None def test_getcustomfile_roundtrip(self, pytester: Pytester) -> None: hello = pytester.makefile(".xxx", hello="world")