Merge pull request #4935 from Zac-HD/warn-unknown-marks
Emit warning on unknown marks via decorator
This commit is contained in:
commit
1c9dcf1f39
|
@ -0,0 +1,2 @@
|
||||||
|
A warning is now emitted when unknown marks are used as a decorator.
|
||||||
|
This is often due to a typo, which can lead to silently broken tests.
|
|
@ -31,7 +31,10 @@ which also serve as documentation.
|
||||||
Raising errors on unknown marks
|
Raising errors on unknown marks
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
Marks can be registered in ``pytest.ini`` like this:
|
Unknown marks applied with the ``@pytest.mark.name_of_the_mark`` decorator
|
||||||
|
will always emit a warning, in order to avoid silently doing something
|
||||||
|
surprising due to mis-typed names. You can disable the warning for custom
|
||||||
|
marks by registering them in ``pytest.ini`` like this:
|
||||||
|
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|
||||||
|
|
|
@ -147,8 +147,7 @@ def pytest_collection_modifyitems(items, config):
|
||||||
|
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
config._old_mark_config = MARK_GEN._config
|
config._old_mark_config = MARK_GEN._config
|
||||||
if config.option.strict:
|
MARK_GEN._config = config
|
||||||
MARK_GEN._config = config
|
|
||||||
|
|
||||||
empty_parameterset = config.getini(EMPTY_PARAMETERSET_OPTION)
|
empty_parameterset = config.getini(EMPTY_PARAMETERSET_OPTION)
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ from ..compat import getfslineno
|
||||||
from ..compat import MappingMixin
|
from ..compat import MappingMixin
|
||||||
from ..compat import NOTSET
|
from ..compat import NOTSET
|
||||||
from _pytest.outcomes import fail
|
from _pytest.outcomes import fail
|
||||||
|
from _pytest.warning_types import UnknownMarkWarning
|
||||||
|
|
||||||
EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark"
|
EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark"
|
||||||
|
|
||||||
|
@ -283,28 +284,38 @@ class MarkGenerator(object):
|
||||||
on the ``test_function`` object. """
|
on the ``test_function`` object. """
|
||||||
|
|
||||||
_config = None
|
_config = None
|
||||||
|
_markers = set()
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
if name[0] == "_":
|
if name[0] == "_":
|
||||||
raise AttributeError("Marker name must NOT start with underscore")
|
raise AttributeError("Marker name must NOT start with underscore")
|
||||||
if self._config is not None:
|
|
||||||
self._check(name)
|
|
||||||
return MarkDecorator(Mark(name, (), {}))
|
|
||||||
|
|
||||||
def _check(self, name):
|
if self._config is not None:
|
||||||
try:
|
# We store a set of markers as a performance optimisation - if a mark
|
||||||
if name in self._markers:
|
# name is in the set we definitely know it, but a mark may be known and
|
||||||
return
|
# not in the set. We therefore start by updating the set!
|
||||||
except AttributeError:
|
if name not in self._markers:
|
||||||
pass
|
for line in self._config.getini("markers"):
|
||||||
self._markers = values = set()
|
# example lines: "skipif(condition): skip the given test if..."
|
||||||
for line in self._config.getini("markers"):
|
# or "hypothesis: tests which use Hypothesis", so to get the
|
||||||
marker = line.split(":", 1)[0]
|
# marker name we we split on both `:` and `(`.
|
||||||
marker = marker.rstrip()
|
marker = line.split(":")[0].split("(")[0].strip()
|
||||||
x = marker.split("(", 1)[0]
|
self._markers.add(marker)
|
||||||
values.add(x)
|
|
||||||
if name not in self._markers:
|
# If the name is not in the set of known marks after updating,
|
||||||
fail("{!r} not a registered marker".format(name), pytrace=False)
|
# then it really is time to issue a warning or an error.
|
||||||
|
if name not in self._markers:
|
||||||
|
if self._config.option.strict:
|
||||||
|
fail("{!r} not a registered marker".format(name), pytrace=False)
|
||||||
|
else:
|
||||||
|
warnings.warn(
|
||||||
|
"Unknown pytest.mark.%s - is this a typo? You can register "
|
||||||
|
"custom marks to avoid this warning - for details, see "
|
||||||
|
"https://docs.pytest.org/en/latest/mark.html" % name,
|
||||||
|
UnknownMarkWarning,
|
||||||
|
)
|
||||||
|
|
||||||
|
return MarkDecorator(Mark(name, (), {}))
|
||||||
|
|
||||||
|
|
||||||
MARK_GEN = MarkGenerator()
|
MARK_GEN = MarkGenerator()
|
||||||
|
|
|
@ -9,6 +9,15 @@ class PytestWarning(UserWarning):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class UnknownMarkWarning(PytestWarning):
|
||||||
|
"""
|
||||||
|
Bases: :class:`PytestWarning`.
|
||||||
|
|
||||||
|
Warning emitted on use of unknown markers.
|
||||||
|
See https://docs.pytest.org/en/latest/mark.html for details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class PytestDeprecationWarning(PytestWarning, DeprecationWarning):
|
class PytestDeprecationWarning(PytestWarning, DeprecationWarning):
|
||||||
"""
|
"""
|
||||||
Bases: :class:`pytest.PytestWarning`, :class:`DeprecationWarning`.
|
Bases: :class:`pytest.PytestWarning`, :class:`DeprecationWarning`.
|
||||||
|
|
2
tox.ini
2
tox.ini
|
@ -165,6 +165,8 @@ filterwarnings =
|
||||||
ignore::pytest.PytestExperimentalApiWarning
|
ignore::pytest.PytestExperimentalApiWarning
|
||||||
# Do not cause SyntaxError for invalid escape sequences in py37.
|
# Do not cause SyntaxError for invalid escape sequences in py37.
|
||||||
default:invalid escape sequence:DeprecationWarning
|
default:invalid escape sequence:DeprecationWarning
|
||||||
|
# ignore use of unregistered marks, because we use many to test the implementation
|
||||||
|
ignore::_pytest.warning_types.UnknownMarkWarning
|
||||||
pytester_example_dir = testing/example_scripts
|
pytester_example_dir = testing/example_scripts
|
||||||
markers =
|
markers =
|
||||||
issue
|
issue
|
||||||
|
|
Loading…
Reference in New Issue