Merge pull request #3044 from RonnyPfannschmidt/parameterset-empty-enable-xfail
empty parameterset - enable opt to xfail
This commit is contained in:
commit
b3247c1d03
|
@ -13,6 +13,8 @@ from _pytest.config import UsageError
|
||||||
from .deprecated import MARK_PARAMETERSET_UNPACKING
|
from .deprecated import MARK_PARAMETERSET_UNPACKING
|
||||||
from .compat import NOTSET, getfslineno
|
from .compat import NOTSET, getfslineno
|
||||||
|
|
||||||
|
EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark"
|
||||||
|
|
||||||
|
|
||||||
def alias(name, warning=None):
|
def alias(name, warning=None):
|
||||||
getter = attrgetter(name)
|
getter = attrgetter(name)
|
||||||
|
@ -73,7 +75,7 @@ class ParameterSet(namedtuple('ParameterSet', 'values, marks, id')):
|
||||||
return cls(argval, marks=newmarks, id=None)
|
return cls(argval, marks=newmarks, id=None)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _for_parameterize(cls, argnames, argvalues, function):
|
def _for_parameterize(cls, argnames, argvalues, function, config):
|
||||||
if not isinstance(argnames, (tuple, list)):
|
if not isinstance(argnames, (tuple, list)):
|
||||||
argnames = [x.strip() for x in argnames.split(",") if x.strip()]
|
argnames = [x.strip() for x in argnames.split(",") if x.strip()]
|
||||||
force_tuple = len(argnames) == 1
|
force_tuple = len(argnames) == 1
|
||||||
|
@ -85,10 +87,7 @@ class ParameterSet(namedtuple('ParameterSet', 'values, marks, id')):
|
||||||
del argvalues
|
del argvalues
|
||||||
|
|
||||||
if not parameters:
|
if not parameters:
|
||||||
fs, lineno = getfslineno(function)
|
mark = get_empty_parameterset_mark(config, argnames, function)
|
||||||
reason = "got empty parameter set %r, function %s at %s:%d" % (
|
|
||||||
argnames, function.__name__, fs, lineno)
|
|
||||||
mark = MARK_GEN.skip(reason=reason)
|
|
||||||
parameters.append(ParameterSet(
|
parameters.append(ParameterSet(
|
||||||
values=(NOTSET,) * len(argnames),
|
values=(NOTSET,) * len(argnames),
|
||||||
marks=[mark],
|
marks=[mark],
|
||||||
|
@ -97,6 +96,20 @@ class ParameterSet(namedtuple('ParameterSet', 'values, marks, id')):
|
||||||
return argnames, parameters
|
return argnames, parameters
|
||||||
|
|
||||||
|
|
||||||
|
def get_empty_parameterset_mark(config, argnames, function):
|
||||||
|
requested_mark = config.getini(EMPTY_PARAMETERSET_OPTION)
|
||||||
|
if requested_mark in ('', None, 'skip'):
|
||||||
|
mark = MARK_GEN.skip
|
||||||
|
elif requested_mark == 'xfail':
|
||||||
|
mark = MARK_GEN.xfail(run=False)
|
||||||
|
else:
|
||||||
|
raise LookupError(requested_mark)
|
||||||
|
fs, lineno = getfslineno(function)
|
||||||
|
reason = "got empty parameter set %r, function %s at %s:%d" % (
|
||||||
|
argnames, function.__name__, fs, lineno)
|
||||||
|
return mark(reason=reason)
|
||||||
|
|
||||||
|
|
||||||
class MarkerError(Exception):
|
class MarkerError(Exception):
|
||||||
|
|
||||||
"""Error in use of a pytest marker/attribute."""
|
"""Error in use of a pytest marker/attribute."""
|
||||||
|
@ -136,6 +149,9 @@ def pytest_addoption(parser):
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.addini("markers", "markers for test functions", 'linelist')
|
parser.addini("markers", "markers for test functions", 'linelist')
|
||||||
|
parser.addini(
|
||||||
|
EMPTY_PARAMETERSET_OPTION,
|
||||||
|
"default marker for empty parametersets")
|
||||||
|
|
||||||
|
|
||||||
def pytest_cmdline_main(config):
|
def pytest_cmdline_main(config):
|
||||||
|
@ -279,6 +295,13 @@ def pytest_configure(config):
|
||||||
if config.option.strict:
|
if config.option.strict:
|
||||||
MARK_GEN._config = config
|
MARK_GEN._config = config
|
||||||
|
|
||||||
|
empty_parameterset = config.getini(EMPTY_PARAMETERSET_OPTION)
|
||||||
|
|
||||||
|
if empty_parameterset not in ('skip', 'xfail', None, ''):
|
||||||
|
raise UsageError(
|
||||||
|
"{!s} must be one of skip and xfail,"
|
||||||
|
" but it is {!r}".format(EMPTY_PARAMETERSET_OPTION, empty_parameterset))
|
||||||
|
|
||||||
|
|
||||||
def pytest_unconfigure(config):
|
def pytest_unconfigure(config):
|
||||||
MARK_GEN._config = getattr(config, '_old_mark_config', None)
|
MARK_GEN._config = getattr(config, '_old_mark_config', None)
|
||||||
|
|
|
@ -786,7 +786,7 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
|
||||||
from _pytest.mark import ParameterSet
|
from _pytest.mark import ParameterSet
|
||||||
from py.io import saferepr
|
from py.io import saferepr
|
||||||
argnames, parameters = ParameterSet._for_parameterize(
|
argnames, parameters = ParameterSet._for_parameterize(
|
||||||
argnames, argvalues, self.function)
|
argnames, argvalues, self.function, self.config)
|
||||||
del argvalues
|
del argvalues
|
||||||
|
|
||||||
if scope is None:
|
if scope is None:
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Introduce ``empty_parameter_set_mark`` ini option to select which mark to apply when ``@pytest.mark.parametrize`` is given an empty set of parameters. Valid options are ``skip`` (default) and ``xfail``. Note that it is planned to change the default to ``xfail`` in future releases as this is considered less error prone.
|
|
@ -346,3 +346,28 @@ passed multiple times. The expected format is ``name=value``. For example::
|
||||||
# content of pytest.ini
|
# content of pytest.ini
|
||||||
[pytest]
|
[pytest]
|
||||||
console_output_style = classic
|
console_output_style = classic
|
||||||
|
|
||||||
|
|
||||||
|
.. confval:: empty_parameter_set_mark
|
||||||
|
|
||||||
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
|
Allows to pick the action for empty parametersets in parameterization
|
||||||
|
|
||||||
|
* ``skip`` skips tests with a empty parameterset (default)
|
||||||
|
* ``xfail`` marks tests with a empty parameterset as xfail(run=False)
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
# content of pytest.ini
|
||||||
|
[pytest]
|
||||||
|
empty_parameter_set_mark = xfail
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The default value of this option is planned to change to ``xfail`` in future releases
|
||||||
|
as this is considered less error prone, see `#3155`_ for more details.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.. _`#3155`: https://github.com/pytest-dev/pytest/issues/3155
|
||||||
|
|
|
@ -14,7 +14,7 @@ PY3 = sys.version_info >= (3, 0)
|
||||||
|
|
||||||
|
|
||||||
class TestMetafunc(object):
|
class TestMetafunc(object):
|
||||||
def Metafunc(self, func):
|
def Metafunc(self, func, config=None):
|
||||||
# the unit tests of this class check if things work correctly
|
# the unit tests of this class check if things work correctly
|
||||||
# on the funcarg level, so we don't need a full blown
|
# on the funcarg level, so we don't need a full blown
|
||||||
# initiliazation
|
# initiliazation
|
||||||
|
@ -26,7 +26,7 @@ class TestMetafunc(object):
|
||||||
|
|
||||||
names = fixtures.getfuncargnames(func)
|
names = fixtures.getfuncargnames(func)
|
||||||
fixtureinfo = FixtureInfo(names)
|
fixtureinfo = FixtureInfo(names)
|
||||||
return python.Metafunc(func, fixtureinfo, None)
|
return python.Metafunc(func, fixtureinfo, config)
|
||||||
|
|
||||||
def test_no_funcargs(self, testdir):
|
def test_no_funcargs(self, testdir):
|
||||||
def function():
|
def function():
|
||||||
|
@ -156,7 +156,19 @@ class TestMetafunc(object):
|
||||||
def test_parametrize_empty_list(self):
|
def test_parametrize_empty_list(self):
|
||||||
def func(y):
|
def func(y):
|
||||||
pass
|
pass
|
||||||
metafunc = self.Metafunc(func)
|
|
||||||
|
class MockConfig(object):
|
||||||
|
def getini(self, name):
|
||||||
|
return ''
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hook(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def pytest_make_parametrize_id(self, **kw):
|
||||||
|
pass
|
||||||
|
|
||||||
|
metafunc = self.Metafunc(func, MockConfig())
|
||||||
metafunc.parametrize("y", [])
|
metafunc.parametrize("y", [])
|
||||||
assert 'skip' == metafunc._calls[0].marks[0].name
|
assert 'skip' == metafunc._calls[0].marks[0].name
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,10 @@ import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.mark import MarkGenerator as Mark, ParameterSet, transfer_markers
|
from _pytest.mark import (
|
||||||
|
MarkGenerator as Mark, ParameterSet, transfer_markers,
|
||||||
|
EMPTY_PARAMETERSET_OPTION,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestMark(object):
|
class TestMark(object):
|
||||||
|
@ -891,3 +894,27 @@ class TestMarkDecorator(object):
|
||||||
])
|
])
|
||||||
def test__eq__(self, lhs, rhs, expected):
|
def test__eq__(self, lhs, rhs, expected):
|
||||||
assert (lhs == rhs) == expected
|
assert (lhs == rhs) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('mark', [None, '', 'skip', 'xfail'])
|
||||||
|
def test_parameterset_for_parametrize_marks(testdir, mark):
|
||||||
|
if mark is not None:
|
||||||
|
testdir.makeini(
|
||||||
|
"[pytest]\n{}={}".format(EMPTY_PARAMETERSET_OPTION, mark))
|
||||||
|
|
||||||
|
config = testdir.parseconfig()
|
||||||
|
from _pytest.mark import pytest_configure, get_empty_parameterset_mark
|
||||||
|
pytest_configure(config)
|
||||||
|
result_mark = get_empty_parameterset_mark(config, ['a'], all)
|
||||||
|
if mark in (None, ''):
|
||||||
|
# normalize to the requested name
|
||||||
|
mark = 'skip'
|
||||||
|
assert result_mark.name == mark
|
||||||
|
assert result_mark.kwargs['reason'].startswith("got empty parameter set ")
|
||||||
|
if mark == 'xfail':
|
||||||
|
assert result_mark.kwargs.get('run') is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_parameterset_for_parametrize_bad_markname(testdir):
|
||||||
|
with pytest.raises(pytest.UsageError):
|
||||||
|
test_parameterset_for_parametrize_marks(testdir, 'bad')
|
||||||
|
|
Loading…
Reference in New Issue