allow re-running of a test item (as exercised by the
pytest-rerunfailures plugins) by re-initializing and removing request/funcargs information in runtestprotocol() - which is a slightly odd place to add funcarg-related functionality but it allows all pytest_runtest_setup/teardown hooks to properly see a valid request/funcarg content on test items.
This commit is contained in:
parent
cf7cae0780
commit
b2cb93e06d
|
@ -1,6 +1,9 @@
|
||||||
Changes between 2.3.4 and 2.3.5dev
|
Changes between 2.3.4 and 2.3.5dev
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
|
- allow re-running of test items / helps to fix pytest-reruntests plugin
|
||||||
|
and also should help to keep less fixture/resource references alive
|
||||||
|
|
||||||
- put captured stdout/stderr into junitxml output even for passing tests
|
- put captured stdout/stderr into junitxml output even for passing tests
|
||||||
(thanks Adam Goucher)
|
(thanks Adam Goucher)
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
#
|
#
|
||||||
__version__ = '2.3.5.dev8'
|
__version__ = '2.3.5.dev16'
|
||||||
|
|
|
@ -917,20 +917,25 @@ class Function(FunctionMixin, pytest.Item, FuncargnamesCompatAttr):
|
||||||
self.cls,
|
self.cls,
|
||||||
funcargs=not isyield)
|
funcargs=not isyield)
|
||||||
self.fixturenames = fi.names_closure
|
self.fixturenames = fi.names_closure
|
||||||
if isyield:
|
if callspec is not None:
|
||||||
assert not callspec, (
|
self.callspec = callspec
|
||||||
|
self._initrequest()
|
||||||
|
|
||||||
|
def _initrequest(self):
|
||||||
|
if self._isyieldedfunction():
|
||||||
|
assert not hasattr(self, "callspec"), (
|
||||||
"yielded functions (deprecated) cannot have funcargs")
|
"yielded functions (deprecated) cannot have funcargs")
|
||||||
self.funcargs = {}
|
self.funcargs = {}
|
||||||
else:
|
else:
|
||||||
if callspec is not None:
|
if hasattr(self, "callspec"):
|
||||||
self.callspec = callspec
|
callspec = self.callspec
|
||||||
self.funcargs = callspec.funcargs or {}
|
self.funcargs = callspec.funcargs.copy()
|
||||||
self._genid = callspec.id
|
self._genid = callspec.id
|
||||||
if hasattr(callspec, "param"):
|
if hasattr(callspec, "param"):
|
||||||
self.param = callspec.param
|
self.param = callspec.param
|
||||||
else:
|
else:
|
||||||
self.funcargs = {}
|
self.funcargs = {}
|
||||||
self._request = req = FixtureRequest(self)
|
self._request = FixtureRequest(self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def function(self):
|
def function(self):
|
||||||
|
|
|
@ -63,12 +63,20 @@ def pytest_runtest_protocol(item, nextitem):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def runtestprotocol(item, log=True, nextitem=None):
|
def runtestprotocol(item, log=True, nextitem=None):
|
||||||
|
hasrequest = hasattr(item, "_request")
|
||||||
|
if hasrequest and not item._request:
|
||||||
|
item._initrequest()
|
||||||
rep = call_and_report(item, "setup", log)
|
rep = call_and_report(item, "setup", log)
|
||||||
reports = [rep]
|
reports = [rep]
|
||||||
if rep.passed:
|
if rep.passed:
|
||||||
reports.append(call_and_report(item, "call", log))
|
reports.append(call_and_report(item, "call", log))
|
||||||
reports.append(call_and_report(item, "teardown", log,
|
reports.append(call_and_report(item, "teardown", log,
|
||||||
nextitem=nextitem))
|
nextitem=nextitem))
|
||||||
|
# after all teardown hooks have been called
|
||||||
|
# want funcargs and request info to go away
|
||||||
|
if hasrequest:
|
||||||
|
item._request = False
|
||||||
|
item.funcargs = None
|
||||||
return reports
|
return reports
|
||||||
|
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item):
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -12,7 +12,7 @@ def main():
|
||||||
name='pytest',
|
name='pytest',
|
||||||
description='py.test: simple powerful testing with Python',
|
description='py.test: simple powerful testing with Python',
|
||||||
long_description = long_description,
|
long_description = long_description,
|
||||||
version='2.3.5.dev8',
|
version='2.3.5.dev16',
|
||||||
url='http://pytest.org',
|
url='http://pytest.org',
|
||||||
license='MIT license',
|
license='MIT license',
|
||||||
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
||||||
|
|
|
@ -117,3 +117,35 @@ class TestMockDecoration:
|
||||||
""")
|
""")
|
||||||
reprec = testdir.inline_run()
|
reprec = testdir.inline_run()
|
||||||
reprec.assertoutcome(passed=2)
|
reprec.assertoutcome(passed=2)
|
||||||
|
|
||||||
|
|
||||||
|
class TestReRunTests:
|
||||||
|
def test_rerun(self, testdir):
|
||||||
|
testdir.makeconftest("""
|
||||||
|
from _pytest.runner import runtestprotocol
|
||||||
|
def pytest_runtest_protocol(item, nextitem):
|
||||||
|
runtestprotocol(item, log=False, nextitem=nextitem)
|
||||||
|
runtestprotocol(item, log=True, nextitem=nextitem)
|
||||||
|
""")
|
||||||
|
testdir.makepyfile("""
|
||||||
|
import pytest
|
||||||
|
count = 0
|
||||||
|
req = None
|
||||||
|
@pytest.fixture
|
||||||
|
def fix(request):
|
||||||
|
global count, req
|
||||||
|
assert request != req
|
||||||
|
req = request
|
||||||
|
print ("fix count %s" % count)
|
||||||
|
count += 1
|
||||||
|
def test_fix(fix):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest("-s")
|
||||||
|
result.stdout.fnmatch_lines("""
|
||||||
|
*fix count 0*
|
||||||
|
*fix count 1*
|
||||||
|
""")
|
||||||
|
result.stdout.fnmatch_lines("""
|
||||||
|
*2 passed*
|
||||||
|
""")
|
||||||
|
|
|
@ -16,6 +16,7 @@ def test_funcarg(testdir):
|
||||||
# pytest_unconfigure has deleted the TempdirHandler already
|
# pytest_unconfigure has deleted the TempdirHandler already
|
||||||
config = item.config
|
config = item.config
|
||||||
config._tmpdirhandler = TempdirHandler(config)
|
config._tmpdirhandler = TempdirHandler(config)
|
||||||
|
item._initrequest()
|
||||||
p = tmpdir(item._request)
|
p = tmpdir(item._request)
|
||||||
assert p.check()
|
assert p.check()
|
||||||
bn = p.basename.strip("0123456789")
|
bn = p.basename.strip("0123456789")
|
||||||
|
|
Loading…
Reference in New Issue