python: remove the `Instance` collector node
This commit is contained in:
parent
17b38259fd
commit
062d91ab47
|
@ -0,0 +1,3 @@
|
||||||
|
The ``pytest.Instance`` collector type has been removed.
|
||||||
|
Importing ``pytest.Instance`` or ``_pytest.python.Instance`` returns a dummy type and emits a deprecation warning.
|
||||||
|
See :ref:`instance-collector-deprecation` for details.
|
|
@ -18,6 +18,25 @@ Deprecated Features
|
||||||
Below is a complete list of all pytest features which are considered deprecated. Using those features will issue
|
Below is a complete list of all pytest features which are considered deprecated. Using those features will issue
|
||||||
:class:`PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters <warnings>`.
|
:class:`PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters <warnings>`.
|
||||||
|
|
||||||
|
.. _instance-collector-deprecation:
|
||||||
|
|
||||||
|
The ``pytest.Instance`` collector
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. versionremoved:: 7.0
|
||||||
|
|
||||||
|
The ``pytest.Instance`` collector type has been removed.
|
||||||
|
|
||||||
|
Previously, Python test methods were collected as :class:`~pytest.Class` -> ``Instance`` -> :class:`~pytest.Function`.
|
||||||
|
Now :class:`~pytest.Class` collects the test methods directly.
|
||||||
|
|
||||||
|
Most plugins which reference ``Instance`` do so in order to ignore or skip it,
|
||||||
|
using a check such as ``if isinstance(node, Instance): return``.
|
||||||
|
Such plugins should simply remove consideration of ``Instance`` on pytest>=7.
|
||||||
|
However, to keep such uses working, a dummy type has been instanted in ``pytest.Instance`` and ``_pytest.python.Instance``,
|
||||||
|
and importing it emits a deprecation warning. This will be removed in pytest 8.
|
||||||
|
|
||||||
|
|
||||||
.. _node-ctor-fspath-deprecation:
|
.. _node-ctor-fspath-deprecation:
|
||||||
|
|
||||||
``fspath`` argument for Node constructors replaced with ``pathlib.Path``
|
``fspath`` argument for Node constructors replaced with ``pathlib.Path``
|
||||||
|
|
|
@ -119,6 +119,11 @@ KEYWORD_MSG_ARG = UnformattedWarning(
|
||||||
"pytest.{func}(msg=...) is now deprecated, use pytest.{func}(reason=...) instead",
|
"pytest.{func}(msg=...) is now deprecated, use pytest.{func}(reason=...) instead",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
INSTANCE_COLLECTOR = PytestDeprecationWarning(
|
||||||
|
"The pytest.Instance collector type is deprecated and is no longer used. "
|
||||||
|
"See https://docs.pytest.org/en/latest/deprecations.html#the-pytest-instance-collector",
|
||||||
|
)
|
||||||
|
|
||||||
# You want to make some `__init__` or function "private".
|
# You want to make some `__init__` or function "private".
|
||||||
#
|
#
|
||||||
# def my_private_function(some, args):
|
# def my_private_function(some, args):
|
||||||
|
|
|
@ -450,10 +450,6 @@ def pytest_unconfigure(config: Config) -> None:
|
||||||
def mangle_test_address(address: str) -> List[str]:
|
def mangle_test_address(address: str) -> List[str]:
|
||||||
path, possible_open_bracket, params = address.partition("[")
|
path, possible_open_bracket, params = address.partition("[")
|
||||||
names = path.split("::")
|
names = path.split("::")
|
||||||
try:
|
|
||||||
names.remove("()")
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
# Convert file path to dotted path.
|
# Convert file path to dotted path.
|
||||||
names[0] = names[0].replace(nodes.SEP, ".")
|
names[0] = names[0].replace(nodes.SEP, ".")
|
||||||
names[0] = re.sub(r"\.py$", "", names[0])
|
names[0] = re.sub(r"\.py$", "", names[0])
|
||||||
|
|
|
@ -781,9 +781,6 @@ class Session(nodes.FSCollector):
|
||||||
submatchnodes.append(r)
|
submatchnodes.append(r)
|
||||||
if submatchnodes:
|
if submatchnodes:
|
||||||
work.append((submatchnodes, matchnames[1:]))
|
work.append((submatchnodes, matchnames[1:]))
|
||||||
# XXX Accept IDs that don't have "()" for class instances.
|
|
||||||
elif len(rep.result) == 1 and rep.result[0].name == "()":
|
|
||||||
work.append((rep.result, matchnames))
|
|
||||||
else:
|
else:
|
||||||
# Report collection failures here to avoid failing to run some test
|
# Report collection failures here to avoid failing to run some test
|
||||||
# specified in the command line because the module could not be
|
# specified in the command line because the module could not be
|
||||||
|
|
|
@ -158,7 +158,7 @@ class KeywordMatcher:
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
for node in item.listchain():
|
for node in item.listchain():
|
||||||
if not isinstance(node, (pytest.Instance, pytest.Session)):
|
if not isinstance(node, pytest.Session):
|
||||||
mapped_names.add(node.name)
|
mapped_names.add(node.name)
|
||||||
|
|
||||||
# Add the names added as extra keywords to current or parent items.
|
# Add the names added as extra keywords to current or parent items.
|
||||||
|
|
|
@ -225,9 +225,7 @@ class Node(metaclass=NodeMeta):
|
||||||
else:
|
else:
|
||||||
if not self.parent:
|
if not self.parent:
|
||||||
raise TypeError("nodeid or parent must be provided")
|
raise TypeError("nodeid or parent must be provided")
|
||||||
self._nodeid = self.parent.nodeid
|
self._nodeid = self.parent.nodeid + "::" + self.name
|
||||||
if self.name != "()":
|
|
||||||
self._nodeid += "::" + self.name
|
|
||||||
|
|
||||||
#: A place where plugins can store information on the node for their
|
#: A place where plugins can store information on the node for their
|
||||||
#: own use.
|
#: own use.
|
||||||
|
|
|
@ -58,6 +58,7 @@ from _pytest.config import hookimpl
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.deprecated import check_ispytest
|
from _pytest.deprecated import check_ispytest
|
||||||
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
||||||
|
from _pytest.deprecated import INSTANCE_COLLECTOR
|
||||||
from _pytest.fixtures import FuncFixtureInfo
|
from _pytest.fixtures import FuncFixtureInfo
|
||||||
from _pytest.main import Session
|
from _pytest.main import Session
|
||||||
from _pytest.mark import MARK_GEN
|
from _pytest.mark import MARK_GEN
|
||||||
|
@ -275,12 +276,6 @@ class PyobjMixin(nodes.Node):
|
||||||
node = self.getparent(Class)
|
node = self.getparent(Class)
|
||||||
return node.obj if node is not None else None
|
return node.obj if node is not None else None
|
||||||
|
|
||||||
@property
|
|
||||||
def instance(self):
|
|
||||||
"""Python instance object this node was collected from (can be None)."""
|
|
||||||
node = self.getparent(Instance)
|
|
||||||
return node.obj if node is not None else None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def obj(self):
|
def obj(self):
|
||||||
"""Underlying Python object."""
|
"""Underlying Python object."""
|
||||||
|
@ -288,7 +283,7 @@ class PyobjMixin(nodes.Node):
|
||||||
if obj is None:
|
if obj is None:
|
||||||
self._obj = obj = self._getobj()
|
self._obj = obj = self._getobj()
|
||||||
# XXX evil hack
|
# XXX evil hack
|
||||||
# used to avoid Instance collector marker duplication
|
# used to avoid Function marker duplication
|
||||||
if self._ALLOW_MARKERS:
|
if self._ALLOW_MARKERS:
|
||||||
self.own_markers.extend(get_unpacked_marks(self.obj))
|
self.own_markers.extend(get_unpacked_marks(self.obj))
|
||||||
return obj
|
return obj
|
||||||
|
@ -310,8 +305,6 @@ class PyobjMixin(nodes.Node):
|
||||||
chain.reverse()
|
chain.reverse()
|
||||||
parts = []
|
parts = []
|
||||||
for node in chain:
|
for node in chain:
|
||||||
if isinstance(node, Instance):
|
|
||||||
continue
|
|
||||||
name = node.name
|
name = node.name
|
||||||
if isinstance(node, Module):
|
if isinstance(node, Module):
|
||||||
name = os.path.splitext(name)[0]
|
name = os.path.splitext(name)[0]
|
||||||
|
@ -410,7 +403,8 @@ class PyCollector(PyobjMixin, nodes.Collector):
|
||||||
|
|
||||||
# Avoid random getattrs and peek in the __dict__ instead.
|
# Avoid random getattrs and peek in the __dict__ instead.
|
||||||
dicts = [getattr(self.obj, "__dict__", {})]
|
dicts = [getattr(self.obj, "__dict__", {})]
|
||||||
for basecls in self.obj.__class__.__mro__:
|
if isinstance(self.obj, type):
|
||||||
|
for basecls in self.obj.__mro__:
|
||||||
dicts.append(basecls.__dict__)
|
dicts.append(basecls.__dict__)
|
||||||
|
|
||||||
# In each class, nodes should be definition ordered. Since Python 3.6,
|
# In each class, nodes should be definition ordered. Since Python 3.6,
|
||||||
|
@ -491,7 +485,6 @@ class PyCollector(PyobjMixin, nodes.Collector):
|
||||||
self,
|
self,
|
||||||
name=subname,
|
name=subname,
|
||||||
callspec=callspec,
|
callspec=callspec,
|
||||||
callobj=funcobj,
|
|
||||||
fixtureinfo=fixtureinfo,
|
fixtureinfo=fixtureinfo,
|
||||||
keywords={callspec.id: True},
|
keywords={callspec.id: True},
|
||||||
originalname=name,
|
originalname=name,
|
||||||
|
@ -776,6 +769,9 @@ class Class(PyCollector):
|
||||||
"""The public constructor."""
|
"""The public constructor."""
|
||||||
return super().from_parent(name=name, parent=parent, **kw)
|
return super().from_parent(name=name, parent=parent, **kw)
|
||||||
|
|
||||||
|
def newinstance(self):
|
||||||
|
return self.obj()
|
||||||
|
|
||||||
def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
|
def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
|
||||||
if not safe_getattr(self.obj, "__test__", True):
|
if not safe_getattr(self.obj, "__test__", True):
|
||||||
return []
|
return []
|
||||||
|
@ -803,7 +799,9 @@ class Class(PyCollector):
|
||||||
self._inject_setup_class_fixture()
|
self._inject_setup_class_fixture()
|
||||||
self._inject_setup_method_fixture()
|
self._inject_setup_method_fixture()
|
||||||
|
|
||||||
return [Instance.from_parent(self, name="()")]
|
self.session._fixturemanager.parsefactories(self.newinstance(), self.nodeid)
|
||||||
|
|
||||||
|
return super().collect()
|
||||||
|
|
||||||
def _inject_setup_class_fixture(self) -> None:
|
def _inject_setup_class_fixture(self) -> None:
|
||||||
"""Inject a hidden autouse, class scoped fixture into the collected class object
|
"""Inject a hidden autouse, class scoped fixture into the collected class object
|
||||||
|
@ -874,25 +872,22 @@ class Class(PyCollector):
|
||||||
self.obj.__pytest_setup_method = xunit_setup_method_fixture
|
self.obj.__pytest_setup_method = xunit_setup_method_fixture
|
||||||
|
|
||||||
|
|
||||||
class Instance(PyCollector):
|
class InstanceDummy:
|
||||||
_ALLOW_MARKERS = False # hack, destroy later
|
"""Instance used to be a node type between Class and Function. It has been
|
||||||
# Instances share the object with their parents in a way
|
removed in pytest 7.0. Some plugins exist which reference `pytest.Instance`
|
||||||
# that duplicates markers instances if not taken out
|
only to ignore it; this dummy class keeps them working. This will be removed
|
||||||
# can be removed at node structure reorganization time.
|
in pytest 8."""
|
||||||
|
|
||||||
def _getobj(self):
|
pass
|
||||||
# TODO: Improve the type of `parent` such that assert/ignore aren't needed.
|
|
||||||
assert self.parent is not None
|
|
||||||
obj = self.parent.obj # type: ignore[attr-defined]
|
|
||||||
return obj()
|
|
||||||
|
|
||||||
def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
|
|
||||||
self.session._fixturemanager.parsefactories(self)
|
|
||||||
return super().collect()
|
|
||||||
|
|
||||||
def newinstance(self):
|
# Note: module __getattr__ only works on Python>=3.7. Unfortunately
|
||||||
self.obj = self._getobj()
|
# we can't provide this deprecation warning on Python 3.6.
|
||||||
return self.obj
|
def __getattr__(name: str) -> object:
|
||||||
|
if name == "Instance":
|
||||||
|
warnings.warn(INSTANCE_COLLECTOR, 2)
|
||||||
|
return InstanceDummy
|
||||||
|
raise AttributeError(f"module {__name__} has no attribute {name}")
|
||||||
|
|
||||||
|
|
||||||
def hasinit(obj: object) -> bool:
|
def hasinit(obj: object) -> bool:
|
||||||
|
@ -1686,9 +1681,23 @@ class Function(PyobjMixin, nodes.Item):
|
||||||
"""Underlying python 'function' object."""
|
"""Underlying python 'function' object."""
|
||||||
return getimfunc(self.obj)
|
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):
|
def _getobj(self):
|
||||||
assert self.parent is not None
|
assert self.parent is not None
|
||||||
return getattr(self.parent.obj, self.originalname) # type: ignore[attr-defined]
|
if isinstance(self.parent, Class):
|
||||||
|
# Each Function gets a fresh class instance.
|
||||||
|
parent_obj = self.parent.newinstance()
|
||||||
|
else:
|
||||||
|
parent_obj = self.parent.obj # type: ignore[attr-defined]
|
||||||
|
return getattr(parent_obj, self.originalname)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _pyfuncitem(self):
|
def _pyfuncitem(self):
|
||||||
|
@ -1700,9 +1709,6 @@ class Function(PyobjMixin, nodes.Item):
|
||||||
self.ihook.pytest_pyfunc_call(pyfuncitem=self)
|
self.ihook.pytest_pyfunc_call(pyfuncitem=self)
|
||||||
|
|
||||||
def setup(self) -> None:
|
def setup(self) -> None:
|
||||||
if isinstance(self.parent, Instance):
|
|
||||||
self.parent.newinstance()
|
|
||||||
self.obj = self._getobj()
|
|
||||||
self._request._fillfixtures()
|
self._request._fillfixtures()
|
||||||
|
|
||||||
def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None:
|
def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None:
|
||||||
|
|
|
@ -756,9 +756,6 @@ class TerminalReporter:
|
||||||
rep.toterminal(self._tw)
|
rep.toterminal(self._tw)
|
||||||
|
|
||||||
def _printcollecteditems(self, items: Sequence[Item]) -> None:
|
def _printcollecteditems(self, items: Sequence[Item]) -> None:
|
||||||
# To print out items and their parent collectors
|
|
||||||
# we take care to leave out Instances aka ()
|
|
||||||
# because later versions are going to get rid of them anyway.
|
|
||||||
if self.config.option.verbose < 0:
|
if self.config.option.verbose < 0:
|
||||||
if self.config.option.verbose < -1:
|
if self.config.option.verbose < -1:
|
||||||
counts = Counter(item.nodeid.split("::", 1)[0] for item in items)
|
counts = Counter(item.nodeid.split("::", 1)[0] for item in items)
|
||||||
|
@ -778,8 +775,6 @@ class TerminalReporter:
|
||||||
stack.pop()
|
stack.pop()
|
||||||
for col in needed_collectors[len(stack) :]:
|
for col in needed_collectors[len(stack) :]:
|
||||||
stack.append(col)
|
stack.append(col)
|
||||||
if col.name == "()": # Skip Instances.
|
|
||||||
continue
|
|
||||||
indent = (len(stack) - 1) * " "
|
indent = (len(stack) - 1) * " "
|
||||||
self._tw.line(f"{indent}{col}")
|
self._tw.line(f"{indent}{col}")
|
||||||
if self.config.option.verbose >= 1:
|
if self.config.option.verbose >= 1:
|
||||||
|
|
|
@ -48,7 +48,6 @@ from _pytest.pytester import RecordedHookCall
|
||||||
from _pytest.pytester import RunResult
|
from _pytest.pytester import RunResult
|
||||||
from _pytest.python import Class
|
from _pytest.python import Class
|
||||||
from _pytest.python import Function
|
from _pytest.python import Function
|
||||||
from _pytest.python import Instance
|
|
||||||
from _pytest.python import Metafunc
|
from _pytest.python import Metafunc
|
||||||
from _pytest.python import Module
|
from _pytest.python import Module
|
||||||
from _pytest.python import Package
|
from _pytest.python import Package
|
||||||
|
@ -77,6 +76,7 @@ from _pytest.warning_types import PytestWarning
|
||||||
|
|
||||||
set_trace = __pytestPDB.set_trace
|
set_trace = __pytestPDB.set_trace
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"__version__",
|
"__version__",
|
||||||
"_fillfuncargs",
|
"_fillfuncargs",
|
||||||
|
@ -106,7 +106,6 @@ __all__ = [
|
||||||
"HookRecorder",
|
"HookRecorder",
|
||||||
"hookspec",
|
"hookspec",
|
||||||
"importorskip",
|
"importorskip",
|
||||||
"Instance",
|
|
||||||
"Item",
|
"Item",
|
||||||
"LineMatcher",
|
"LineMatcher",
|
||||||
"LogCaptureFixture",
|
"LogCaptureFixture",
|
||||||
|
@ -153,3 +152,12 @@ __all__ = [
|
||||||
"xfail",
|
"xfail",
|
||||||
"yield_fixture",
|
"yield_fixture",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def __getattr__(name: str) -> object:
|
||||||
|
if name == "Instance":
|
||||||
|
# The import emits a deprecation warning.
|
||||||
|
from _pytest.python import Instance
|
||||||
|
|
||||||
|
return Instance
|
||||||
|
raise AttributeError(f"module {__name__} has no attribute {name}")
|
||||||
|
|
|
@ -11,7 +11,6 @@ COLLECT_FAKEMODULE_ATTRIBUTES = [
|
||||||
"Collector",
|
"Collector",
|
||||||
"Module",
|
"Module",
|
||||||
"Function",
|
"Function",
|
||||||
"Instance",
|
|
||||||
"Session",
|
"Session",
|
||||||
"Item",
|
"Item",
|
||||||
"Class",
|
"Class",
|
||||||
|
|
|
@ -286,3 +286,21 @@ def test_node_ctor_fspath_argument_is_deprecated(pytester: Pytester) -> None:
|
||||||
parent=mod.parent,
|
parent=mod.parent,
|
||||||
fspath=legacy_path("bla"),
|
fspath=legacy_path("bla"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
sys.version_info < (3, 7),
|
||||||
|
reason="This deprecation can only be emitted on python>=3.7",
|
||||||
|
)
|
||||||
|
def test_importing_instance_is_deprecated(pytester: Pytester) -> None:
|
||||||
|
with pytest.warns(
|
||||||
|
pytest.PytestDeprecationWarning,
|
||||||
|
match=re.escape("The pytest.Instance collector type is deprecated"),
|
||||||
|
):
|
||||||
|
pytest.Instance
|
||||||
|
|
||||||
|
with pytest.warns(
|
||||||
|
pytest.PytestDeprecationWarning,
|
||||||
|
match=re.escape("The pytest.Instance collector type is deprecated"),
|
||||||
|
):
|
||||||
|
from _pytest.python import Instance # noqa: F401
|
||||||
|
|
|
@ -12,7 +12,7 @@ from _pytest.monkeypatch import MonkeyPatch
|
||||||
from _pytest.nodes import Collector
|
from _pytest.nodes import Collector
|
||||||
from _pytest.pytester import Pytester
|
from _pytest.pytester import Pytester
|
||||||
from _pytest.python import Class
|
from _pytest.python import Class
|
||||||
from _pytest.python import Instance
|
from _pytest.python import Function
|
||||||
|
|
||||||
|
|
||||||
class TestModule:
|
class TestModule:
|
||||||
|
@ -585,7 +585,7 @@ class TestFunction:
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
colitems = modcol.collect()[0].collect()[0].collect()
|
colitems = modcol.collect()[0].collect()
|
||||||
assert colitems[0].name == "test1[a-c]"
|
assert colitems[0].name == "test1[a-c]"
|
||||||
assert colitems[1].name == "test1[b-c]"
|
assert colitems[1].name == "test1[b-c]"
|
||||||
assert colitems[2].name == "test2[a-c]"
|
assert colitems[2].name == "test2[a-c]"
|
||||||
|
@ -1183,19 +1183,26 @@ class TestReportInfo:
|
||||||
modcol = pytester.getmodulecol(
|
modcol = pytester.getmodulecol(
|
||||||
"""
|
"""
|
||||||
# lineno 0
|
# lineno 0
|
||||||
class TestClass(object):
|
class TestClass:
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
return "this is not an int"
|
return "this is not an int"
|
||||||
|
|
||||||
|
def __class_getattr__(cls, name):
|
||||||
|
return "this is not an int"
|
||||||
|
|
||||||
def intest_foo(self):
|
def intest_foo(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def test_bar(self):
|
||||||
|
pass
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
classcol = pytester.collect_by_name(modcol, "TestClass")
|
classcol = pytester.collect_by_name(modcol, "TestClass")
|
||||||
assert isinstance(classcol, Class)
|
assert isinstance(classcol, Class)
|
||||||
instance = list(classcol.collect())[0]
|
path, lineno, msg = classcol.reportinfo()
|
||||||
assert isinstance(instance, Instance)
|
func = list(classcol.collect())[0]
|
||||||
path, lineno, msg = instance.reportinfo()
|
assert isinstance(func, Function)
|
||||||
|
path, lineno, msg = func.reportinfo()
|
||||||
|
|
||||||
|
|
||||||
def test_customized_python_discovery(pytester: Pytester) -> None:
|
def test_customized_python_discovery(pytester: Pytester) -> None:
|
||||||
|
|
|
@ -5,6 +5,7 @@ from _pytest import runner
|
||||||
from _pytest._code import getfslineno
|
from _pytest._code import getfslineno
|
||||||
from _pytest.fixtures import getfixturemarker
|
from _pytest.fixtures import getfixturemarker
|
||||||
from _pytest.pytester import Pytester
|
from _pytest.pytester import Pytester
|
||||||
|
from _pytest.python import Function
|
||||||
|
|
||||||
|
|
||||||
class TestOEJSKITSpecials:
|
class TestOEJSKITSpecials:
|
||||||
|
@ -475,3 +476,28 @@ class TestParameterize:
|
||||||
)
|
)
|
||||||
res = pytester.runpytest("--collect-only")
|
res = pytester.runpytest("--collect-only")
|
||||||
res.stdout.fnmatch_lines(["*spam-2*", "*ham-2*"])
|
res.stdout.fnmatch_lines(["*spam-2*", "*ham-2*"])
|
||||||
|
|
||||||
|
|
||||||
|
def test_function_instance(pytester: Pytester) -> None:
|
||||||
|
items = pytester.getitems(
|
||||||
|
"""
|
||||||
|
def test_func(): pass
|
||||||
|
class TestIt:
|
||||||
|
def test_method(self): pass
|
||||||
|
@classmethod
|
||||||
|
def test_class(cls): pass
|
||||||
|
@staticmethod
|
||||||
|
def test_static(): pass
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
assert len(items) == 3
|
||||||
|
assert isinstance(items[0], Function)
|
||||||
|
assert items[0].name == "test_func"
|
||||||
|
assert items[0].instance is None
|
||||||
|
assert isinstance(items[1], Function)
|
||||||
|
assert items[1].name == "test_method"
|
||||||
|
assert items[1].instance is not None
|
||||||
|
assert items[1].instance.__class__.__name__ == "TestIt"
|
||||||
|
assert isinstance(items[2], Function)
|
||||||
|
assert items[2].name == "test_static"
|
||||||
|
assert items[2].instance is None
|
||||||
|
|
|
@ -74,9 +74,7 @@ class TestCollector:
|
||||||
)
|
)
|
||||||
cls = pytester.collect_by_name(modcol, "TestClass")
|
cls = pytester.collect_by_name(modcol, "TestClass")
|
||||||
assert isinstance(cls, pytest.Class)
|
assert isinstance(cls, pytest.Class)
|
||||||
instance = pytester.collect_by_name(cls, "()")
|
fn = pytester.collect_by_name(cls, "test_foo")
|
||||||
assert isinstance(instance, pytest.Instance)
|
|
||||||
fn = pytester.collect_by_name(instance, "test_foo")
|
|
||||||
assert isinstance(fn, pytest.Function)
|
assert isinstance(fn, pytest.Function)
|
||||||
|
|
||||||
module_parent = fn.getparent(pytest.Module)
|
module_parent = fn.getparent(pytest.Module)
|
||||||
|
|
|
@ -936,7 +936,7 @@ class TestPython:
|
||||||
def test_mangle_test_address() -> None:
|
def test_mangle_test_address() -> None:
|
||||||
from _pytest.junitxml import mangle_test_address
|
from _pytest.junitxml import mangle_test_address
|
||||||
|
|
||||||
address = "::".join(["a/my.py.thing.py", "Class", "()", "method", "[a-1-::]"])
|
address = "::".join(["a/my.py.thing.py", "Class", "method", "[a-1-::]"])
|
||||||
newnames = mangle_test_address(address)
|
newnames = mangle_test_address(address)
|
||||||
assert newnames == ["a.my.py.thing", "Class", "method", "[a-1-::]"]
|
assert newnames == ["a.my.py.thing", "Class", "method", "[a-1-::]"]
|
||||||
|
|
||||||
|
|
|
@ -272,8 +272,10 @@ def test_nose_setup_ordering(pytester: Pytester) -> None:
|
||||||
class TestClass(object):
|
class TestClass(object):
|
||||||
def setup(self):
|
def setup(self):
|
||||||
assert visited
|
assert visited
|
||||||
|
self.visited_cls = True
|
||||||
def test_first(self):
|
def test_first(self):
|
||||||
pass
|
assert visited
|
||||||
|
assert self.visited_cls
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
|
|
|
@ -227,7 +227,7 @@ class TestNewSession(SessionTests):
|
||||||
started = reprec.getcalls("pytest_collectstart")
|
started = reprec.getcalls("pytest_collectstart")
|
||||||
finished = reprec.getreports("pytest_collectreport")
|
finished = reprec.getreports("pytest_collectreport")
|
||||||
assert len(started) == len(finished)
|
assert len(started) == len(finished)
|
||||||
assert len(started) == 8
|
assert len(started) == 6
|
||||||
colfail = [x for x in finished if x.failed]
|
colfail = [x for x in finished if x.failed]
|
||||||
assert len(colfail) == 1
|
assert len(colfail) == 1
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue