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:
holger krekel 2013-04-22 10:35:48 +02:00
parent cf7cae0780
commit b2cb93e06d
7 changed files with 57 additions and 8 deletions

View File

@ -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)

View File

@ -1,2 +1,2 @@
# #
__version__ = '2.3.5.dev8' __version__ = '2.3.5.dev16'

View File

@ -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):

View File

@ -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):

View File

@ -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'],

View File

@ -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*
""")

View File

@ -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")