fix issue91 introduce new py.test.xfail(reason) helper
to imperatively mark a test as expected to fail. Can be used from within setup and test functions. This is useful especially for parametrized tests when certain configurations are expected-to-fail. In this case the declarative approach with the @py.test.mark.xfail cannot be used as it would mark all configurations as xfail. --HG-- branch : trunk
This commit is contained in:
parent
eac0345689
commit
925f75088d
|
@ -9,6 +9,14 @@ Changes between 1.3.0 and 1.3.1
|
||||||
- fix issue95: late-import zlib so that it's not required
|
- fix issue95: late-import zlib so that it's not required
|
||||||
for general py.test startup.
|
for general py.test startup.
|
||||||
|
|
||||||
|
- fix issue91: introduce new py.test.xfail(reason) helper
|
||||||
|
to imperatively mark a test as expected to fail. Can
|
||||||
|
be used from within setup and test functions. This is
|
||||||
|
useful especially for parametrized tests when certain
|
||||||
|
configurations are expected-to-fail. In this case the
|
||||||
|
declarative approach with the @py.test.mark.xfail cannot
|
||||||
|
be used as it would mark all configurations as xfail.
|
||||||
|
|
||||||
- make py.test.cmdline.main() return the exitstatus
|
- make py.test.cmdline.main() return the exitstatus
|
||||||
instead of raising (which is still done by py.cmdline.pytest())
|
instead of raising (which is still done by py.cmdline.pytest())
|
||||||
and make it so that py.test.cmdline.main() can be called
|
and make it so that py.test.cmdline.main() can be called
|
||||||
|
|
|
@ -10,6 +10,7 @@ def pytest_namespace():
|
||||||
'skip' : skip,
|
'skip' : skip,
|
||||||
'importorskip' : importorskip,
|
'importorskip' : importorskip,
|
||||||
'fail' : fail,
|
'fail' : fail,
|
||||||
|
'xfail' : xfail,
|
||||||
'exit' : exit,
|
'exit' : exit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,6 +296,10 @@ class Failed(OutcomeException):
|
||||||
""" raised from an explicit call to py.test.fail() """
|
""" raised from an explicit call to py.test.fail() """
|
||||||
__module__ = 'builtins'
|
__module__ = 'builtins'
|
||||||
|
|
||||||
|
class XFailed(OutcomeException):
|
||||||
|
""" raised from an explicit call to py.test.xfail() """
|
||||||
|
__module__ = 'builtins'
|
||||||
|
|
||||||
class ExceptionFailure(Failed):
|
class ExceptionFailure(Failed):
|
||||||
""" raised by py.test.raises on an exception-assertion mismatch. """
|
""" raised by py.test.raises on an exception-assertion mismatch. """
|
||||||
def __init__(self, expr, expected, msg=None, excinfo=None):
|
def __init__(self, expr, expected, msg=None, excinfo=None):
|
||||||
|
@ -335,6 +340,14 @@ def fail(msg=""):
|
||||||
|
|
||||||
fail.Exception = Failed
|
fail.Exception = Failed
|
||||||
|
|
||||||
|
def xfail(reason=""):
|
||||||
|
""" xfail an executing test or setup functions, taking an optional
|
||||||
|
reason string.
|
||||||
|
"""
|
||||||
|
__tracebackhide__ = True
|
||||||
|
raise XFailed(reason)
|
||||||
|
xfail.Exception = XFailed
|
||||||
|
|
||||||
def raises(ExpectedException, *args, **kwargs):
|
def raises(ExpectedException, *args, **kwargs):
|
||||||
""" if args[0] is callable: raise AssertionError if calling it with
|
""" if args[0] is callable: raise AssertionError if calling it with
|
||||||
the remaining arguments does not raise the expected exception.
|
the remaining arguments does not raise the expected exception.
|
||||||
|
|
|
@ -185,9 +185,17 @@ def pytest_runtest_setup(item):
|
||||||
def pytest_runtest_makereport(__multicall__, item, call):
|
def pytest_runtest_makereport(__multicall__, item, call):
|
||||||
if not isinstance(item, py.test.collect.Function):
|
if not isinstance(item, py.test.collect.Function):
|
||||||
return
|
return
|
||||||
evalxfail = getattr(item, '_evalxfail', None)
|
if not (call.excinfo and
|
||||||
if not evalxfail:
|
call.excinfo.errisinstance(py.test.xfail.Exception)):
|
||||||
return
|
evalxfail = getattr(item, '_evalxfail', None)
|
||||||
|
if not evalxfail:
|
||||||
|
return
|
||||||
|
if call.excinfo and call.excinfo.errisinstance(py.test.xfail.Exception):
|
||||||
|
rep = __multicall__.execute()
|
||||||
|
rep.keywords['xfail'] = "reason: " + call.excinfo.value.msg
|
||||||
|
rep.skipped = True
|
||||||
|
rep.failed = False
|
||||||
|
return rep
|
||||||
if call.when == "setup":
|
if call.when == "setup":
|
||||||
rep = __multicall__.execute()
|
rep = __multicall__.execute()
|
||||||
if rep.skipped and evalxfail.istrue():
|
if rep.skipped and evalxfail.istrue():
|
||||||
|
|
|
@ -169,6 +169,43 @@ class TestXFail:
|
||||||
])
|
])
|
||||||
assert result.ret == 1
|
assert result.ret == 1
|
||||||
|
|
||||||
|
def test_xfail_imperative(self, testdir):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
import py
|
||||||
|
def test_this():
|
||||||
|
py.test.xfail("hello")
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest(p)
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*1 xfailed*",
|
||||||
|
])
|
||||||
|
result = testdir.runpytest(p, "-rx")
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*XFAIL*test_this*reason:*hello*",
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_xfail_imperative_in_setup_function(self, testdir):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
import py
|
||||||
|
def setup_function(function):
|
||||||
|
py.test.xfail("hello")
|
||||||
|
|
||||||
|
def test_this():
|
||||||
|
assert 0
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest(p)
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*1 xfailed*",
|
||||||
|
])
|
||||||
|
result = testdir.runpytest(p, "-rx")
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*XFAIL*test_this*reason:*hello*",
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TestSkipif:
|
class TestSkipif:
|
||||||
def test_skipif_conditional(self, testdir):
|
def test_skipif_conditional(self, testdir):
|
||||||
item = testdir.getitem("""
|
item = testdir.getitem("""
|
||||||
|
|
Loading…
Reference in New Issue