drops special testcontext object in favour of "old" request object, simplifying communication and code for the 2.2-2.3 transition. also modify docs and examples.
This commit is contained in:
parent
f6b555f5ad
commit
646c2c6001
16
CHANGELOG
16
CHANGELOG
|
@ -9,19 +9,9 @@ Changes between 2.2.4 and 2.3.0.dev
|
||||||
it is constructed correctly from the original current working dir.
|
it is constructed correctly from the original current working dir.
|
||||||
- fix "python setup.py test" example to cause a proper "errno" return
|
- fix "python setup.py test" example to cause a proper "errno" return
|
||||||
- fix issue165 - fix broken doc links and mention stackoverflow for FAQ
|
- fix issue165 - fix broken doc links and mention stackoverflow for FAQ
|
||||||
- fix issue139 - merge FuncargRequest and Item API such that
|
- fix issue139 - introduce @pytest.factory which allows direct scoping
|
||||||
funcarg-functionality is now directly available on the "item"
|
and parametrization of funcarg factories. Introduce new @pytest.setup
|
||||||
object passed to the various pytest_runtest hooks. This allows more
|
marker to allow the writing of setup functions which accept funcargs.
|
||||||
sensitive behaviour of e.g. the pytest-django plugin which previously
|
|
||||||
had no full access to all instantiated funcargs.
|
|
||||||
This internal API re-organisation is a fully backward compatible
|
|
||||||
change: existing factories accepting a "request" object will
|
|
||||||
get a Function "item" object which carries the same API. In fact,
|
|
||||||
the FuncargRequest API (or rather then a ResourceRequestAPI)
|
|
||||||
could be available for all collection and item nodes but this is
|
|
||||||
left for later consideration because it would render the documentation
|
|
||||||
invalid and the "funcarg" naming sounds odd in context of
|
|
||||||
directory, file, class, etc. nodes.
|
|
||||||
- catch unicode-issues when writing failure representations
|
- catch unicode-issues when writing failure representations
|
||||||
to terminal to prevent the whole session from crashing
|
to terminal to prevent the whole session from crashing
|
||||||
- fix xfail/skip confusion: a skip-mark or an imperative pytest.skip
|
- fix xfail/skip confusion: a skip-mark or an imperative pytest.skip
|
||||||
|
|
|
@ -912,31 +912,51 @@ class Function(FunctionMixin, pytest.Item):
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash((self.parent, self.name))
|
return hash((self.parent, self.name))
|
||||||
|
|
||||||
|
scope2props = dict(session=())
|
||||||
|
scope2props["module"] = ("fspath", "module")
|
||||||
|
scope2props["class"] = scope2props["module"] + ("cls",)
|
||||||
|
scope2props["instance"] = scope2props["class"] + ("instance", )
|
||||||
|
scope2props["function"] = scope2props["instance"] + ("function", "keywords")
|
||||||
|
|
||||||
|
def scopeproperty(name=None, doc=None):
|
||||||
|
def decoratescope(func):
|
||||||
|
scopename = name or func.__name__
|
||||||
|
def provide(self):
|
||||||
|
if func.__name__ in scope2props[self.scope]:
|
||||||
|
return func(self)
|
||||||
|
raise AttributeError("%s not available in %s-scoped context" % (
|
||||||
|
scopename, self.scope))
|
||||||
|
return property(provide, None, None, func.__doc__)
|
||||||
|
return decoratescope
|
||||||
|
|
||||||
|
def pytest_funcarg__request(__request__):
|
||||||
|
return __request__
|
||||||
|
|
||||||
|
#def pytest_funcarg__testcontext(__request__):
|
||||||
|
# return __request__
|
||||||
|
|
||||||
class FuncargRequest:
|
class FuncargRequest:
|
||||||
""" (old-style) A request for function arguments from a test function.
|
""" A request for function arguments from a test or setup function.
|
||||||
|
|
||||||
Note that there is an optional ``param`` attribute in case
|
A request object gives access to attributes of the requesting
|
||||||
there was an invocation to metafunc.addcall(param=...).
|
test context. It has an optional ``param`` attribute in case
|
||||||
If no such call was done in a ``pytest_generate_tests``
|
of parametrization.
|
||||||
hook, the attribute will not be present. Note that
|
|
||||||
as of pytest-2.3 you probably rather want to use the
|
|
||||||
testcontext object and mark your factory with a ``@pytest.factory``
|
|
||||||
marker.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, pyfuncitem):
|
def __init__(self, pyfuncitem):
|
||||||
self._pyfuncitem = pyfuncitem
|
self._pyfuncitem = pyfuncitem
|
||||||
if hasattr(pyfuncitem, '_requestparam'):
|
if hasattr(pyfuncitem, '_requestparam'):
|
||||||
self.param = pyfuncitem._requestparam
|
self.param = pyfuncitem._requestparam
|
||||||
|
#: Scope string, one of "function", "cls", "module", "session"
|
||||||
|
self.scope = "function"
|
||||||
self.getparent = pyfuncitem.getparent
|
self.getparent = pyfuncitem.getparent
|
||||||
self._funcargs = self._pyfuncitem.funcargs.copy()
|
self._funcargs = self._pyfuncitem.funcargs.copy()
|
||||||
|
self._funcargs["__request__"] = self
|
||||||
self._name2factory = {}
|
self._name2factory = {}
|
||||||
self.funcargmanager = pyfuncitem.session.funcargmanager
|
self.funcargmanager = pyfuncitem.session.funcargmanager
|
||||||
self._currentarg = None
|
self._currentarg = None
|
||||||
self.funcargnames = getfuncargnames(self.function)
|
self.funcargnames = getfuncargnames(self.function)
|
||||||
self.parentid = pyfuncitem.parent.nodeid
|
self.parentid = pyfuncitem.parent.nodeid
|
||||||
self.scope = "function"
|
|
||||||
self._factorystack = []
|
self._factorystack = []
|
||||||
|
|
||||||
def _getfaclist(self, argname):
|
def _getfaclist(self, argname):
|
||||||
|
@ -956,21 +976,42 @@ class FuncargRequest:
|
||||||
self.parentid)
|
self.parentid)
|
||||||
return facdeflist
|
return facdeflist
|
||||||
|
|
||||||
def raiseerror(self, msg):
|
|
||||||
""" raise a FuncargLookupError with the given message. """
|
|
||||||
raise self.funcargmanager.FuncargLookupError(self.function, msg)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
def config(self):
|
||||||
|
""" the pytest config object associated with this request. """
|
||||||
|
return self._pyfuncitem.config
|
||||||
|
|
||||||
|
|
||||||
|
@scopeproperty()
|
||||||
def function(self):
|
def function(self):
|
||||||
""" function object of the test invocation. """
|
""" test function object if the request has a per-function scope. """
|
||||||
return self._pyfuncitem.obj
|
return self._pyfuncitem.obj
|
||||||
|
|
||||||
|
@scopeproperty("class")
|
||||||
|
def cls(self):
|
||||||
|
""" class (can be None) where the test function was collected. """
|
||||||
|
clscol = self._pyfuncitem.getparent(pytest.Class)
|
||||||
|
if clscol:
|
||||||
|
return clscol.obj
|
||||||
|
|
||||||
|
@scopeproperty()
|
||||||
|
def instance(self):
|
||||||
|
""" instance (can be None) on which test function was collected. """
|
||||||
|
return py.builtin._getimself(self.function)
|
||||||
|
|
||||||
|
@scopeproperty()
|
||||||
|
def module(self):
|
||||||
|
""" python module object where the test function was collected. """
|
||||||
|
return self._pyfuncitem.getparent(pytest.Module).obj
|
||||||
|
|
||||||
|
@scopeproperty()
|
||||||
|
def fspath(self):
|
||||||
|
""" the file system path of the test module which collected this test. """
|
||||||
|
return self._pyfuncitem.fspath
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def keywords(self):
|
def keywords(self):
|
||||||
""" keywords of the test function item.
|
""" keywords of the test function item. """
|
||||||
|
|
||||||
.. versionadded:: 2.0
|
|
||||||
"""
|
|
||||||
return self._pyfuncitem.keywords
|
return self._pyfuncitem.keywords
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -978,41 +1019,23 @@ class FuncargRequest:
|
||||||
""" pytest session object. """
|
""" pytest session object. """
|
||||||
return self._pyfuncitem.session
|
return self._pyfuncitem.session
|
||||||
|
|
||||||
@property
|
|
||||||
def module(self):
|
|
||||||
""" module where the test function was collected. """
|
|
||||||
return self._pyfuncitem.getparent(pytest.Module).obj
|
|
||||||
|
|
||||||
@property
|
|
||||||
def cls(self):
|
|
||||||
""" class (can be None) where the test function was collected. """
|
|
||||||
clscol = self._pyfuncitem.getparent(pytest.Class)
|
|
||||||
if clscol:
|
|
||||||
return clscol.obj
|
|
||||||
@property
|
|
||||||
def instance(self):
|
|
||||||
""" instance (can be None) on which test function was collected. """
|
|
||||||
return py.builtin._getimself(self.function)
|
|
||||||
|
|
||||||
@property
|
def addfinalizer(self, finalizer):
|
||||||
def config(self):
|
"""add finalizer/teardown function to be called after the
|
||||||
""" the pytest config object associated with this request. """
|
last test within the requesting test context finished
|
||||||
return self._pyfuncitem.config
|
execution. """
|
||||||
|
self._addfinalizer(finalizer, scope=self.scope)
|
||||||
|
|
||||||
@property
|
def _addfinalizer(self, finalizer, scope):
|
||||||
def fspath(self):
|
if scope != "function" and hasattr(self, "param"):
|
||||||
""" the file system path of the test module which collected this test. """
|
# parametrized resources are sorted by param
|
||||||
return self._pyfuncitem.fspath
|
# so we rather store finalizers per (argname, param)
|
||||||
|
colitem = (self._currentarg, self.param)
|
||||||
def _fillfuncargs(self):
|
else:
|
||||||
if self.funcargnames:
|
colitem = self._getscopeitem(scope)
|
||||||
assert not getattr(self._pyfuncitem, '_args', None), (
|
self._pyfuncitem.session._setupstate.addfinalizer(
|
||||||
"yielded functions cannot have funcargs")
|
finalizer=finalizer, colitem=colitem)
|
||||||
while self.funcargnames:
|
|
||||||
argname = self.funcargnames.pop(0)
|
|
||||||
if argname not in self._pyfuncitem.funcargs:
|
|
||||||
self._pyfuncitem.funcargs[argname] = \
|
|
||||||
self.getfuncargvalue(argname)
|
|
||||||
|
|
||||||
def applymarker(self, marker):
|
def applymarker(self, marker):
|
||||||
""" Apply a marker to a single test function invocation.
|
""" Apply a marker to a single test function invocation.
|
||||||
|
@ -1026,11 +1049,33 @@ class FuncargRequest:
|
||||||
raise ValueError("%r is not a py.test.mark.* object")
|
raise ValueError("%r is not a py.test.mark.* object")
|
||||||
self._pyfuncitem.keywords[marker.markname] = marker
|
self._pyfuncitem.keywords[marker.markname] = marker
|
||||||
|
|
||||||
|
def raiseerror(self, msg):
|
||||||
|
""" raise a FuncargLookupError with the given message. """
|
||||||
|
raise self.funcargmanager.FuncargLookupError(self.function, msg)
|
||||||
|
|
||||||
|
|
||||||
|
def _fillfuncargs(self):
|
||||||
|
if self.funcargnames:
|
||||||
|
assert not getattr(self._pyfuncitem, '_args', None), (
|
||||||
|
"yielded functions cannot have funcargs")
|
||||||
|
while self.funcargnames:
|
||||||
|
argname = self.funcargnames.pop(0)
|
||||||
|
if argname not in self._pyfuncitem.funcargs:
|
||||||
|
self._pyfuncitem.funcargs[argname] = \
|
||||||
|
self.getfuncargvalue(argname)
|
||||||
|
|
||||||
|
|
||||||
|
def _callsetup(self):
|
||||||
|
self.funcargmanager.ensure_setupcalls(self)
|
||||||
|
|
||||||
def cached_setup(self, setup, teardown=None, scope="module", extrakey=None):
|
def cached_setup(self, setup, teardown=None, scope="module", extrakey=None):
|
||||||
""" Return a testing resource managed by ``setup`` &
|
""" (deprecated) Return a testing resource managed by ``setup`` &
|
||||||
``teardown`` calls. ``scope`` and ``extrakey`` determine when the
|
``teardown`` calls. ``scope`` and ``extrakey`` determine when the
|
||||||
``teardown`` function will be called so that subsequent calls to
|
``teardown`` function will be called so that subsequent calls to
|
||||||
``setup`` would recreate the resource.
|
``setup`` would recreate the resource. With pytest-2.3 you
|
||||||
|
do not need ``cached_setup()`` as you can directly declare a scope
|
||||||
|
on a funcarg factory and register a finalizer through
|
||||||
|
``request.addfinalizer()``.
|
||||||
|
|
||||||
:arg teardown: function receiving a previously setup resource.
|
:arg teardown: function receiving a previously setup resource.
|
||||||
:arg setup: a no-argument function creating a resource.
|
:arg setup: a no-argument function creating a resource.
|
||||||
|
@ -1061,16 +1106,19 @@ class FuncargRequest:
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
|
||||||
def _callsetup(self):
|
|
||||||
self.funcargmanager.ensure_setupcalls(self)
|
|
||||||
|
|
||||||
def getfuncargvalue(self, argname):
|
def getfuncargvalue(self, argname):
|
||||||
""" Retrieve a function argument by name for this test
|
""" (deprecated) Retrieve a function argument by name for this test
|
||||||
function invocation. This allows one function argument factory
|
function invocation. This allows one function argument factory
|
||||||
to call another function argument factory. If there are two
|
to call another function argument factory. If there are two
|
||||||
funcarg factories for the same test function argument the first
|
funcarg factories for the same test function argument the first
|
||||||
factory may use ``getfuncargvalue`` to call the second one and
|
factory may use ``getfuncargvalue`` to call the second one and
|
||||||
do something additional with the resource.
|
do something additional with the resource.
|
||||||
|
|
||||||
|
**Note**, however, that starting with
|
||||||
|
pytest-2.3 it is easier and better to directly state the needed
|
||||||
|
funcarg in the factory signature. This will also work seemlessly
|
||||||
|
with parametrization and the new resource setup optimizations.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self._funcargs[argname]
|
return self._funcargs[argname]
|
||||||
|
@ -1091,12 +1139,7 @@ class FuncargRequest:
|
||||||
factory_kwargs = {}
|
factory_kwargs = {}
|
||||||
def fillfactoryargs():
|
def fillfactoryargs():
|
||||||
for newname in newnames:
|
for newname in newnames:
|
||||||
if newname == "testcontext":
|
val = self.getfuncargvalue(newname)
|
||||||
val = TestContextResource(self)
|
|
||||||
elif newname == "request" and not factorydef.new:
|
|
||||||
val = self
|
|
||||||
else:
|
|
||||||
val = self.getfuncargvalue(newname)
|
|
||||||
factory_kwargs[newname] = val
|
factory_kwargs[newname] = val
|
||||||
|
|
||||||
node = self._pyfuncitem
|
node = self._pyfuncitem
|
||||||
|
@ -1161,21 +1204,6 @@ class FuncargRequest:
|
||||||
return self._pyfuncitem.getparent(pytest.Module)
|
return self._pyfuncitem.getparent(pytest.Module)
|
||||||
raise ValueError("unknown finalization scope %r" %(scope,))
|
raise ValueError("unknown finalization scope %r" %(scope,))
|
||||||
|
|
||||||
def addfinalizer(self, finalizer):
|
|
||||||
"""add finalizer function to be called after test function
|
|
||||||
finished execution. """
|
|
||||||
self._addfinalizer(finalizer, scope=self.scope)
|
|
||||||
|
|
||||||
def _addfinalizer(self, finalizer, scope):
|
|
||||||
if scope != "function" and hasattr(self, "param"):
|
|
||||||
# parametrized resources are sorted by param
|
|
||||||
# so we rather store finalizers per (argname, param)
|
|
||||||
colitem = (self._currentarg, self.param)
|
|
||||||
else:
|
|
||||||
colitem = self._getscopeitem(scope)
|
|
||||||
self._pyfuncitem.session._setupstate.addfinalizer(
|
|
||||||
finalizer=finalizer, colitem=colitem)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<FuncargRequest for %r>" %(self._pyfuncitem)
|
return "<FuncargRequest for %r>" %(self._pyfuncitem)
|
||||||
|
|
||||||
|
@ -1396,17 +1424,13 @@ class FuncargManager:
|
||||||
if setupcall.active:
|
if setupcall.active:
|
||||||
continue
|
continue
|
||||||
request._factorystack.append(setupcall)
|
request._factorystack.append(setupcall)
|
||||||
|
mp = monkeypatch()
|
||||||
try:
|
try:
|
||||||
testcontext = TestContextSetup(request, setupcall)
|
#mp.setattr(request, "_setupcall", setupcall, raising=False)
|
||||||
|
mp.setattr(request, "scope", setupcall.scope)
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
for name in setupcall.funcargnames:
|
for name in setupcall.funcargnames:
|
||||||
try:
|
kwargs[name] = request.getfuncargvalue(name)
|
||||||
kwargs[name] = request.getfuncargvalue(name)
|
|
||||||
except FuncargLookupError:
|
|
||||||
if name == "testcontext":
|
|
||||||
kwargs[name] = testcontext
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
scope = setupcall.scope or "function"
|
scope = setupcall.scope or "function"
|
||||||
scol = setupcall.scopeitem = request._getscopeitem(scope)
|
scol = setupcall.scopeitem = request._getscopeitem(scope)
|
||||||
self.session._setupstate.addfinalizer(setupcall.finish, scol)
|
self.session._setupstate.addfinalizer(setupcall.finish, scol)
|
||||||
|
@ -1414,6 +1438,7 @@ class FuncargManager:
|
||||||
self.addargfinalizer(setupcall.finish, argname)
|
self.addargfinalizer(setupcall.finish, argname)
|
||||||
setupcall.execute(kwargs)
|
setupcall.execute(kwargs)
|
||||||
finally:
|
finally:
|
||||||
|
mp.undo()
|
||||||
request._factorystack.remove(setupcall)
|
request._factorystack.remove(setupcall)
|
||||||
|
|
||||||
def addargfinalizer(self, finalizer, argname):
|
def addargfinalizer(self, finalizer, argname):
|
||||||
|
@ -1427,69 +1452,6 @@ class FuncargManager:
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
scope2props = dict(session=())
|
|
||||||
scope2props["module"] = ("fspath", "module")
|
|
||||||
scope2props["class"] = scope2props["module"] + ("cls",)
|
|
||||||
scope2props["function"] = scope2props["class"] + ("function", "keywords")
|
|
||||||
|
|
||||||
def scopeprop(attr, name=None, doc=None):
|
|
||||||
if doc is None:
|
|
||||||
doc = ("%s of underlying test context, may not exist "
|
|
||||||
"if the testcontext has a higher scope" % (attr,))
|
|
||||||
name = name or attr
|
|
||||||
def get(self):
|
|
||||||
if name in scope2props[self.scope]:
|
|
||||||
return getattr(self._request, name)
|
|
||||||
raise AttributeError("%s not available in %s-scoped context" % (
|
|
||||||
name, self.scope))
|
|
||||||
return property(get, doc=doc)
|
|
||||||
|
|
||||||
def rprop(attr, doc=None):
|
|
||||||
if doc is None:
|
|
||||||
doc = "%s of underlying test context" % attr
|
|
||||||
return property(lambda x: getattr(x._request, attr), doc=doc)
|
|
||||||
|
|
||||||
class TestContext(object):
|
|
||||||
""" Basic objects of the current testing context. """
|
|
||||||
def __init__(self, request, scope):
|
|
||||||
self._request = request
|
|
||||||
self.scope = scope
|
|
||||||
|
|
||||||
# no getfuncargvalue(), cached_setup, applymarker helpers here
|
|
||||||
# on purpose
|
|
||||||
|
|
||||||
config = rprop("config", "pytest config object.")
|
|
||||||
session = rprop("session", "pytest session object.")
|
|
||||||
|
|
||||||
function = scopeprop("function")
|
|
||||||
module = scopeprop("module")
|
|
||||||
cls = scopeprop("class", "cls")
|
|
||||||
instance = scopeprop("instance")
|
|
||||||
fspath = scopeprop("fspath")
|
|
||||||
#keywords = scopeprop("keywords")
|
|
||||||
|
|
||||||
class TestContextSetup(TestContext):
|
|
||||||
def __init__(self, request, setupcall):
|
|
||||||
self._setupcall = setupcall
|
|
||||||
self._finalizers = []
|
|
||||||
super(TestContextSetup, self).__init__(request, setupcall.scope)
|
|
||||||
|
|
||||||
def addfinalizer(self, finalizer):
|
|
||||||
""" Add a finalizer to be called after the last test in the
|
|
||||||
test context executes. """
|
|
||||||
self._setupcall.addfinalizer(finalizer)
|
|
||||||
|
|
||||||
class TestContextResource(TestContext):
|
|
||||||
param = rprop("param")
|
|
||||||
|
|
||||||
def __init__(self, request):
|
|
||||||
super(TestContextResource, self).__init__(request, request.scope)
|
|
||||||
|
|
||||||
def addfinalizer(self, finalizer):
|
|
||||||
""" Add a finalizer to be called after the last test in the
|
|
||||||
test context executes. """
|
|
||||||
self._request.addfinalizer(finalizer)
|
|
||||||
|
|
||||||
|
|
||||||
class SetupCall:
|
class SetupCall:
|
||||||
""" a container/helper for managing calls to setup functions. """
|
""" a container/helper for managing calls to setup functions. """
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.factory("session")
|
@pytest.factory("session")
|
||||||
def setup(testcontext):
|
def setup(request):
|
||||||
setup = CostlySetup()
|
setup = CostlySetup()
|
||||||
testcontext.addfinalizer(setup.finalize)
|
request.addfinalizer(setup.finalize)
|
||||||
return setup
|
return setup
|
||||||
|
|
||||||
class CostlySetup:
|
class CostlySetup:
|
||||||
|
|
|
@ -6,13 +6,13 @@ import py, pytest
|
||||||
|
|
||||||
pythonlist = ['python2.4', 'python2.5', 'python2.6', 'python2.7', 'python2.8']
|
pythonlist = ['python2.4', 'python2.5', 'python2.6', 'python2.7', 'python2.8']
|
||||||
@pytest.factory(params=pythonlist)
|
@pytest.factory(params=pythonlist)
|
||||||
def python1(testcontext, tmpdir):
|
def python1(request, tmpdir):
|
||||||
picklefile = tmpdir.join("data.pickle")
|
picklefile = tmpdir.join("data.pickle")
|
||||||
return Python(testcontext.param, picklefile)
|
return Python(request.param, picklefile)
|
||||||
|
|
||||||
@pytest.factory(params=pythonlist)
|
@pytest.factory(params=pythonlist)
|
||||||
def python2(testcontext, python1):
|
def python2(request, python1):
|
||||||
return Python(testcontext.param, python1.picklefile)
|
return Python(request.param, python1.picklefile)
|
||||||
|
|
||||||
class Python:
|
class Python:
|
||||||
def __init__(self, version, picklefile):
|
def __init__(self, version, picklefile):
|
||||||
|
|
|
@ -66,15 +66,15 @@ Direct scoping of funcarg factories
|
||||||
Instead of calling cached_setup(), you can use the :ref:`@pytest.factory <@pytest.factory>` decorator and directly state the scope::
|
Instead of calling cached_setup(), you can use the :ref:`@pytest.factory <@pytest.factory>` decorator and directly state the scope::
|
||||||
|
|
||||||
@pytest.factory(scope="session")
|
@pytest.factory(scope="session")
|
||||||
def db(testcontext):
|
def db(request):
|
||||||
# factory will only be invoked once per session -
|
# factory will only be invoked once per session -
|
||||||
db = DataBase()
|
db = DataBase()
|
||||||
testcontext.addfinalizer(db.destroy) # destroy when session is finished
|
request.addfinalizer(db.destroy) # destroy when session is finished
|
||||||
return db
|
return db
|
||||||
|
|
||||||
This factory implementation does not need to call ``cached_setup()`` anymore
|
This factory implementation does not need to call ``cached_setup()`` anymore
|
||||||
because it will only be invoked once per session. Moreover, the
|
because it will only be invoked once per session. Moreover, the
|
||||||
``testcontext.addfinalizer()`` registers a finalizer according to the specified
|
``request.addfinalizer()`` registers a finalizer according to the specified
|
||||||
resource scope on which the factory function is operating.
|
resource scope on which the factory function is operating.
|
||||||
|
|
||||||
|
|
||||||
|
@ -88,29 +88,29 @@ parametrization, i.e. calling a test multiple times with different value
|
||||||
sets. pytest-2.3 introduces a decorator for use on the factory itself::
|
sets. pytest-2.3 introduces a decorator for use on the factory itself::
|
||||||
|
|
||||||
@pytest.factory(params=["mysql", "pg"])
|
@pytest.factory(params=["mysql", "pg"])
|
||||||
def db(testcontext):
|
def db(request):
|
||||||
... # use testcontext.param
|
... # use request.param
|
||||||
|
|
||||||
Here the factory will be invoked twice (with the respective "mysql"
|
Here the factory will be invoked twice (with the respective "mysql"
|
||||||
and "pg" values set as ``testcontext.param`` attributes) and and all of
|
and "pg" values set as ``request.param`` attributes) and and all of
|
||||||
the tests requiring "db" will run twice as well. The "mysql" and
|
the tests requiring "db" will run twice as well. The "mysql" and
|
||||||
"pg" values will also be used for reporting the test-invocation variants.
|
"pg" values will also be used for reporting the test-invocation variants.
|
||||||
|
|
||||||
This new way of parametrizing funcarg factories should in many cases
|
This new way of parametrizing funcarg factories should in many cases
|
||||||
allow to re-use already written factories because effectively
|
allow to re-use already written factories because effectively
|
||||||
``testcontext.param`` are already the parametrization attribute for test
|
``request.param`` are already the parametrization attribute for test
|
||||||
functions/classes were parametrized via
|
functions/classes were parametrized via
|
||||||
:py:func:`~_pytest.python.Metafunc.parametrize(indirect=True)` calls.
|
:py:func:`~_pytest.python.Metafunc.parametrize(indirect=True)` calls.
|
||||||
|
|
||||||
Of course it's perfectly fine to combine parametrization and scoping::
|
Of course it's perfectly fine to combine parametrization and scoping::
|
||||||
|
|
||||||
@pytest.factory(scope="session", params=["mysql", "pg"])
|
@pytest.factory(scope="session", params=["mysql", "pg"])
|
||||||
def db(testcontext):
|
def db(request):
|
||||||
if testcontext.param == "mysql":
|
if request.param == "mysql":
|
||||||
db = MySQL()
|
db = MySQL()
|
||||||
elif testcontext.param == "pg":
|
elif request.param == "pg":
|
||||||
db = PG()
|
db = PG()
|
||||||
testcontext.addfinalizer(db.destroy) # destroy when session is finished
|
request.addfinalizer(db.destroy) # destroy when session is finished
|
||||||
return db
|
return db
|
||||||
|
|
||||||
This would execute all tests requiring the per-session "db" resource twice,
|
This would execute all tests requiring the per-session "db" resource twice,
|
||||||
|
@ -126,7 +126,7 @@ denotes the name under which the resource can be accessed as a function
|
||||||
argument::
|
argument::
|
||||||
|
|
||||||
@pytest.factory()
|
@pytest.factory()
|
||||||
def db(testcontext):
|
def db(request):
|
||||||
...
|
...
|
||||||
|
|
||||||
The name under which the funcarg resource can be requested is ``db``.
|
The name under which the funcarg resource can be requested is ``db``.
|
||||||
|
|
|
@ -9,44 +9,45 @@ funcargs: resource injection and parametrization
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
pytest-2.3 introduces major refinements to the original funcarg
|
pytest-2.3 introduces major refinements to the test setup and funcarg
|
||||||
mechanism introduced to pytest-2.0. While the old way
|
mechanisms introduced to pytest-2.0. All pre-2.3 usages remain
|
||||||
remains fully supported, it is recommended to use the refined
|
supported and several use cases, among them scoping and parametrization
|
||||||
mechanisms. See also the `compatibility notes`_ and the detailed
|
of funcarg resources, are now easier to accomplish. For more background,
|
||||||
:ref:`reasoning for the new funcarg and setup functions <funcargcompare>`.
|
see `compatibility notes`_ and the detailed :ref:`reasoning for the new
|
||||||
|
funcarg and setup functions <funcargcompare>`.
|
||||||
|
|
||||||
.. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection
|
.. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
====================
|
====================
|
||||||
|
|
||||||
py.test supports the injection of objects into test and setup functions
|
pytest supports the injection of test resources into test and setup functions
|
||||||
and flexibly control their life cycle in relation to the overall test
|
and flexibly control their life cycle in relation to the overall test
|
||||||
execution. Moreover, you can run a test function multiple times
|
execution. Moreover, tests can get executed multiple times if you have
|
||||||
injecting different objects.
|
different variants of test resources to test with.
|
||||||
|
|
||||||
The basic mechanism for injecting objects is called the *funcarg
|
The basic mechanism for injecting objects is called the *funcarg
|
||||||
mechanism* because objects are **injected** when a test or setup
|
mechanism* because objects are injected when a test or setup
|
||||||
function states it as an **argument**. The injected argument is
|
**function** states it as an **argument**. The injected argument
|
||||||
created by a call to a registered **funcarg factory**. This approach is
|
is created by a call to a registered **funcarg factory** for each argument
|
||||||
an example of `Dependency Injection`_ and helps to de-couple test code
|
name. This mechanism is an example of `Dependency Injection`_
|
||||||
from the setup of required objects: at test writing time you do not need
|
and helps to de-couple test code from the setup of required
|
||||||
to care for the details of where and how your required resources are
|
objects: at test writing time you do not need to care for the details of
|
||||||
constructed, if they are shared on a per-class, module or session basis,
|
where and how your required test resources are constructed, if they are
|
||||||
or if your test function is invoked multiple times with differently
|
shared on a per-class, module or session basis, or if your test function
|
||||||
configured resource instances.
|
is invoked multiple times with differently configured resource
|
||||||
|
instances.
|
||||||
|
|
||||||
|
Funcarg dependency injection allows to organise test resources
|
||||||
|
in a modular explicit way so that test functions state their needs
|
||||||
|
in their signature. pytest additionally offers powerful xunit-style
|
||||||
|
:ref:`setup functions <setup functions>` for the cases where you need
|
||||||
|
to create implicit test state that is not passed explicitely to test functions.
|
||||||
|
|
||||||
When a test function is invoked multiple times with different arguments we
|
When a test function is invoked multiple times with different arguments we
|
||||||
speak of **parametrized testing**. This is useful if you want to test
|
speak of **parametrized testing**. You can use it e. g. to repeatedly run test
|
||||||
e.g. against different database backends or want to write a parametrized
|
functions against different database backends or to check that certain
|
||||||
test function, checking that certain inputs lead to certain outputs.
|
inputs lead to certain outputs.
|
||||||
You can parametrize funcarg factories, parametrize test function
|
|
||||||
arguments or even implement your own parametrization scheme through a
|
|
||||||
plugin hook.
|
|
||||||
|
|
||||||
pytest additionally offers powerful xunit-style :ref:`setup functions <setup
|
|
||||||
functions>` for the cases where you need to create implicit test state
|
|
||||||
that is not passed explicitely to test functions.
|
|
||||||
|
|
||||||
Concretely, there are three main means of funcarg management:
|
Concretely, there are three main means of funcarg management:
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ Concretely, there are three main means of funcarg management:
|
||||||
their scoping and parametrization. Factories can themselves
|
their scoping and parametrization. Factories can themselves
|
||||||
receive resources through their function arguments, easing
|
receive resources through their function arguments, easing
|
||||||
the setup of `interdependent resources`_. Factories can use
|
the setup of `interdependent resources`_. Factories can use
|
||||||
the special `testcontext`_ object to access details from where
|
the special `request`_ object to access details from where
|
||||||
the factory or setup function is called and for registering finalizers.
|
the factory or setup function is called and for registering finalizers.
|
||||||
|
|
||||||
* a `@pytest.mark.parametrize`_ marker for executing test functions
|
* a `@pytest.mark.parametrize`_ marker for executing test functions
|
||||||
|
@ -103,8 +104,8 @@ factory function. Running the tests looks like this::
|
||||||
|
|
||||||
$ py.test test_simplefactory.py
|
$ py.test test_simplefactory.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev8
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov
|
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
||||||
collecting ... collected 1 items
|
collecting ... collected 1 items
|
||||||
|
|
||||||
test_simplefactory.py F
|
test_simplefactory.py F
|
||||||
|
@ -119,7 +120,7 @@ factory function. Running the tests looks like this::
|
||||||
E assert 42 == 17
|
E assert 42 == 17
|
||||||
|
|
||||||
test_simplefactory.py:8: AssertionError
|
test_simplefactory.py:8: AssertionError
|
||||||
========================= 1 failed in 0.02 seconds =========================
|
========================= 1 failed in 0.01 seconds =========================
|
||||||
|
|
||||||
This shows that the test function was called with a ``myfuncarg``
|
This shows that the test function was called with a ``myfuncarg``
|
||||||
argument value of ``42`` and the assert fails as expected. Here is
|
argument value of ``42`` and the assert fails as expected. Here is
|
||||||
|
@ -186,7 +187,7 @@ test session::
|
||||||
import smtplib
|
import smtplib
|
||||||
|
|
||||||
@pytest.factory(scope="session")
|
@pytest.factory(scope="session")
|
||||||
def smtp(testcontext):
|
def smtp(request):
|
||||||
return smtplib.SMTP("merlinux.eu")
|
return smtplib.SMTP("merlinux.eu")
|
||||||
|
|
||||||
The name of the factory is ``smtp`` (the factory function name)
|
The name of the factory is ``smtp`` (the factory function name)
|
||||||
|
@ -214,7 +215,7 @@ inspect what is going on and can now run the tests::
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
________________________________ test_ehlo _________________________________
|
________________________________ test_ehlo _________________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP instance at 0x25e7b48>
|
smtp = <smtplib.SMTP instance at 0x31bce18>
|
||||||
|
|
||||||
def test_ehlo(smtp):
|
def test_ehlo(smtp):
|
||||||
response = smtp.ehlo()
|
response = smtp.ehlo()
|
||||||
|
@ -226,7 +227,7 @@ inspect what is going on and can now run the tests::
|
||||||
test_module.py:5: AssertionError
|
test_module.py:5: AssertionError
|
||||||
________________________________ test_noop _________________________________
|
________________________________ test_noop _________________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP instance at 0x25e7b48>
|
smtp = <smtplib.SMTP instance at 0x31bce18>
|
||||||
|
|
||||||
def test_noop(smtp):
|
def test_noop(smtp):
|
||||||
response = smtp.noop()
|
response = smtp.noop()
|
||||||
|
@ -235,7 +236,7 @@ inspect what is going on and can now run the tests::
|
||||||
E assert 0
|
E assert 0
|
||||||
|
|
||||||
test_module.py:10: AssertionError
|
test_module.py:10: AssertionError
|
||||||
2 failed in 0.20 seconds
|
2 failed in 0.26 seconds
|
||||||
|
|
||||||
you see the two ``assert 0`` failing and can also see that
|
you see the two ``assert 0`` failing and can also see that
|
||||||
the same (session-scoped) object was passed into the two test functions.
|
the same (session-scoped) object was passed into the two test functions.
|
||||||
|
@ -247,7 +248,7 @@ Parametrizing a session-shared funcarg resource
|
||||||
Extending the previous example, we can flag the factory to create
|
Extending the previous example, we can flag the factory to create
|
||||||
two ``smtp`` values which will cause all tests using it to
|
two ``smtp`` values which will cause all tests using it to
|
||||||
run twice with two different values. The factory function gets
|
run twice with two different values. The factory function gets
|
||||||
access to each parameter through the special `testcontext`_ object::
|
access to each parameter through the special `request`_ object::
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -255,11 +256,11 @@ access to each parameter through the special `testcontext`_ object::
|
||||||
|
|
||||||
@pytest.factory(scope="session",
|
@pytest.factory(scope="session",
|
||||||
params=["merlinux.eu", "mail.python.org"])
|
params=["merlinux.eu", "mail.python.org"])
|
||||||
def smtp(testcontext):
|
def smtp(request):
|
||||||
return smtplib.SMTP(testcontext.param)
|
return smtplib.SMTP(request.param)
|
||||||
|
|
||||||
The main change is the definition of a ``params`` list in the
|
The main change is the definition of a ``params`` list in the
|
||||||
``factory``-marker and the ``testcontext.param`` access within the
|
``factory``-marker and the ``request.param`` access within the
|
||||||
factory function. No test function code needs to change.
|
factory function. No test function code needs to change.
|
||||||
So let's just do another run::
|
So let's just do another run::
|
||||||
|
|
||||||
|
@ -269,7 +270,7 @@ So let's just do another run::
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
__________________________ test_ehlo[merlinux.eu] __________________________
|
__________________________ test_ehlo[merlinux.eu] __________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP instance at 0x2960050>
|
smtp = <smtplib.SMTP instance at 0x28dc5a8>
|
||||||
|
|
||||||
def test_ehlo(smtp):
|
def test_ehlo(smtp):
|
||||||
response = smtp.ehlo()
|
response = smtp.ehlo()
|
||||||
|
@ -281,7 +282,7 @@ So let's just do another run::
|
||||||
test_module.py:5: AssertionError
|
test_module.py:5: AssertionError
|
||||||
__________________________ test_noop[merlinux.eu] __________________________
|
__________________________ test_noop[merlinux.eu] __________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP instance at 0x2960050>
|
smtp = <smtplib.SMTP instance at 0x28dc5a8>
|
||||||
|
|
||||||
def test_noop(smtp):
|
def test_noop(smtp):
|
||||||
response = smtp.noop()
|
response = smtp.noop()
|
||||||
|
@ -292,7 +293,7 @@ So let's just do another run::
|
||||||
test_module.py:10: AssertionError
|
test_module.py:10: AssertionError
|
||||||
________________________ test_ehlo[mail.python.org] ________________________
|
________________________ test_ehlo[mail.python.org] ________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP instance at 0x296a128>
|
smtp = <smtplib.SMTP instance at 0x28e3e18>
|
||||||
|
|
||||||
def test_ehlo(smtp):
|
def test_ehlo(smtp):
|
||||||
response = smtp.ehlo()
|
response = smtp.ehlo()
|
||||||
|
@ -303,7 +304,7 @@ So let's just do another run::
|
||||||
test_module.py:4: AssertionError
|
test_module.py:4: AssertionError
|
||||||
________________________ test_noop[mail.python.org] ________________________
|
________________________ test_noop[mail.python.org] ________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP instance at 0x296a128>
|
smtp = <smtplib.SMTP instance at 0x28e3e18>
|
||||||
|
|
||||||
def test_noop(smtp):
|
def test_noop(smtp):
|
||||||
response = smtp.noop()
|
response = smtp.noop()
|
||||||
|
@ -312,7 +313,7 @@ So let's just do another run::
|
||||||
E assert 0
|
E assert 0
|
||||||
|
|
||||||
test_module.py:10: AssertionError
|
test_module.py:10: AssertionError
|
||||||
4 failed in 6.00 seconds
|
4 failed in 6.17 seconds
|
||||||
|
|
||||||
We get four failures because we are running the two tests twice with
|
We get four failures because we are running the two tests twice with
|
||||||
different ``smtp`` instantiations as defined on the factory.
|
different ``smtp`` instantiations as defined on the factory.
|
||||||
|
@ -324,7 +325,7 @@ Adding a finalizer to the parametrized resource
|
||||||
|
|
||||||
Further extending the ``smtp`` example, we now want to properly
|
Further extending the ``smtp`` example, we now want to properly
|
||||||
close a smtp server connection after the last test using it
|
close a smtp server connection after the last test using it
|
||||||
has been run. We can do this by calling the ``testcontext.addfinalizer()``
|
has been run. We can do this by calling the ``request.addfinalizer()``
|
||||||
helper::
|
helper::
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
|
@ -333,12 +334,12 @@ helper::
|
||||||
|
|
||||||
@pytest.factory(scope="session",
|
@pytest.factory(scope="session",
|
||||||
params=["merlinux.eu", "mail.python.org"])
|
params=["merlinux.eu", "mail.python.org"])
|
||||||
def smtp(testcontext):
|
def smtp(request):
|
||||||
smtp = smtplib.SMTP(testcontext.param)
|
smtp = smtplib.SMTP(request.param)
|
||||||
def fin():
|
def fin():
|
||||||
print ("finalizing %s" % smtp)
|
print ("finalizing %s" % smtp)
|
||||||
smtp.close()
|
smtp.close()
|
||||||
testcontext.addfinalizer(fin)
|
request.addfinalizer(fin)
|
||||||
return smtp
|
return smtp
|
||||||
|
|
||||||
We also add a print call and then run py.test without the default
|
We also add a print call and then run py.test without the default
|
||||||
|
@ -347,9 +348,9 @@ output capturing and disabled traceback reporting::
|
||||||
$ py.test -s -q --tb=no
|
$ py.test -s -q --tb=no
|
||||||
collecting ... collected 4 items
|
collecting ... collected 4 items
|
||||||
FFFF
|
FFFF
|
||||||
4 failed in 5.62 seconds
|
4 failed in 6.40 seconds
|
||||||
finalizing <smtplib.SMTP instance at 0x2d60368>
|
finalizing <smtplib.SMTP instance at 0x125d3b0>
|
||||||
finalizing <smtplib.SMTP instance at 0x2d68e60>
|
finalizing <smtplib.SMTP instance at 0x1265b90>
|
||||||
|
|
||||||
We see that the two ``smtp`` instances are finalized appropriately.
|
We see that the two ``smtp`` instances are finalized appropriately.
|
||||||
|
|
||||||
|
@ -360,8 +361,8 @@ You can also look at what tests pytest collects without running them::
|
||||||
|
|
||||||
$ py.test --collectonly
|
$ py.test --collectonly
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev8
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov
|
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
||||||
collecting ... collected 4 items
|
collecting ... collected 4 items
|
||||||
<Module 'test_module.py'>
|
<Module 'test_module.py'>
|
||||||
<Function 'test_ehlo[merlinux.eu]'>
|
<Function 'test_ehlo[merlinux.eu]'>
|
||||||
|
@ -369,7 +370,7 @@ You can also look at what tests pytest collects without running them::
|
||||||
<Function 'test_ehlo[mail.python.org]'>
|
<Function 'test_ehlo[mail.python.org]'>
|
||||||
<Function 'test_noop[mail.python.org]'>
|
<Function 'test_noop[mail.python.org]'>
|
||||||
|
|
||||||
============================= in 0.02 seconds =============================
|
============================= in 0.01 seconds =============================
|
||||||
|
|
||||||
Note that pytest orders your test run by resource usage, minimizing
|
Note that pytest orders your test run by resource usage, minimizing
|
||||||
the number of active resources at any given time.
|
the number of active resources at any given time.
|
||||||
|
@ -404,15 +405,15 @@ the ``smtp`` resource by listing it as an input parameter. Let's run this::
|
||||||
|
|
||||||
$ py.test -v test_appsetup.py
|
$ py.test -v test_appsetup.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev8 -- /home/hpk/venv/1/bin/python
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python
|
||||||
cachedir: /home/hpk/tmp/doc-exec-414/.cache
|
cachedir: /home/hpk/tmp/doc-exec-423/.cache
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov
|
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
||||||
collecting ... collected 2 items
|
collecting ... collected 2 items
|
||||||
|
|
||||||
test_appsetup.py:12: test_exists[merlinux.eu] PASSED
|
test_appsetup.py:12: test_exists[merlinux.eu] PASSED
|
||||||
test_appsetup.py:12: test_exists[mail.python.org] PASSED
|
test_appsetup.py:12: test_exists[mail.python.org] PASSED
|
||||||
|
|
||||||
========================= 2 passed in 5.37 seconds =========================
|
========================= 2 passed in 6.82 seconds =========================
|
||||||
|
|
||||||
Due to the parametrization of ``smtp`` the test will run twice with two
|
Due to the parametrization of ``smtp`` the test will run twice with two
|
||||||
different ``App`` instances and respective smtp servers. There is no
|
different ``App`` instances and respective smtp servers. There is no
|
||||||
|
@ -445,17 +446,17 @@ to show the flow of calls::
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.factory(scope="module", params=["mod1", "mod2"])
|
@pytest.factory(scope="module", params=["mod1", "mod2"])
|
||||||
def modarg(testcontext):
|
def modarg(request):
|
||||||
param = testcontext.param
|
param = request.param
|
||||||
print "create", param
|
print "create", param
|
||||||
def fin():
|
def fin():
|
||||||
print "fin", param
|
print "fin", param
|
||||||
testcontext.addfinalizer(fin)
|
request.addfinalizer(fin)
|
||||||
return param
|
return param
|
||||||
|
|
||||||
@pytest.factory(scope="function", params=[1,2])
|
@pytest.factory(scope="function", params=[1,2])
|
||||||
def otherarg(testcontext):
|
def otherarg(request):
|
||||||
return testcontext.param
|
return request.param
|
||||||
|
|
||||||
def test_0(otherarg):
|
def test_0(otherarg):
|
||||||
print " test0", otherarg
|
print " test0", otherarg
|
||||||
|
@ -468,9 +469,9 @@ Let's run the tests in verbose mode and with looking at the print-output::
|
||||||
|
|
||||||
$ py.test -v -s test_module.py
|
$ py.test -v -s test_module.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev8 -- /home/hpk/venv/1/bin/python
|
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python
|
||||||
cachedir: /home/hpk/tmp/doc-exec-414/.cache
|
cachedir: /home/hpk/tmp/doc-exec-423/.cache
|
||||||
plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov
|
plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov
|
||||||
collecting ... collected 8 items
|
collecting ... collected 8 items
|
||||||
|
|
||||||
test_module.py:16: test_0[1] PASSED
|
test_module.py:16: test_0[1] PASSED
|
||||||
|
@ -501,26 +502,29 @@ a re-ordering of test execution. The finalizer for the ``mod1`` parametrized
|
||||||
resource was executed before the ``mod2`` resource was setup.
|
resource was executed before the ``mod2`` resource was setup.
|
||||||
|
|
||||||
.. currentmodule:: _pytest.python
|
.. currentmodule:: _pytest.python
|
||||||
.. _`testcontext`:
|
.. _`request`:
|
||||||
|
|
||||||
``testcontext``: interacting with test context
|
``request``: interacting with test invocation context
|
||||||
---------------------------------------------------
|
-------------------------------------------------------
|
||||||
|
|
||||||
The ``testcontext`` object may be used by `@pytest.factory`_ or
|
The ``request`` object may be received by `@pytest.factory`_ or
|
||||||
:ref:`@pytest.setup <setup>` marked functions. It contains
|
:ref:`@pytest.setup <setup>` marked functions and provides methods
|
||||||
information relating to the test context within which the marked
|
|
||||||
function executes. Moreover, you can call
|
|
||||||
``testcontext.addfinalizer(myfinalizer)`` in order to trigger a call to
|
|
||||||
``myfinalizer`` after the last test in the test context has executed.
|
|
||||||
If passed to a parametrized factory ``testcontext.param`` will contain a
|
|
||||||
parameter (one value out of the ``params`` list specified with the
|
|
||||||
`@pytest.factory`_ marker).
|
|
||||||
|
|
||||||
.. autoclass:: _pytest.python.TestContext()
|
* to inspect attributes of the requesting test context, such as
|
||||||
|
``function``, ``cls``, ``module``, ``session`` and the pytest
|
||||||
|
``config`` object. A request object passed to a parametrized factory
|
||||||
|
will also carry a ``request.param`` object (A parametrized factory and
|
||||||
|
all of its dependent tests will be called with each of the factory-specified
|
||||||
|
``params``).
|
||||||
|
|
||||||
|
* to add finalizers/teardowns to be invoked when the last
|
||||||
|
test of the requesting test context executes
|
||||||
|
|
||||||
|
|
||||||
|
.. autoclass:: _pytest.python.FuncargRequest()
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. _`test generators`:
|
.. _`test generators`:
|
||||||
.. _`parametrizing-tests`:
|
.. _`parametrizing-tests`:
|
||||||
.. _`parametrized test functions`:
|
.. _`parametrized test functions`:
|
||||||
|
@ -566,7 +570,6 @@ two ``(input, output)`` arguments of ``test_eval`` function so the latter
|
||||||
will be run three times::
|
will be run three times::
|
||||||
|
|
||||||
$ py.test -q
|
$ py.test -q
|
||||||
|
|
||||||
collecting ... collected 13 items
|
collecting ... collected 13 items
|
||||||
....F........
|
....F........
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
|
@ -585,7 +588,7 @@ will be run three times::
|
||||||
E + where 54 = eval('6*9')
|
E + where 54 = eval('6*9')
|
||||||
|
|
||||||
test_expectation.py:8: AssertionError
|
test_expectation.py:8: AssertionError
|
||||||
1 failed, 12 passed in 5.76 seconds
|
1 failed, 12 passed in 6.41 seconds
|
||||||
|
|
||||||
As expected only one pair of input/output values fails the simple test function.
|
As expected only one pair of input/output values fails the simple test function.
|
||||||
As usual you can see the ``input`` and ``output`` values in the traceback.
|
As usual you can see the ``input`` and ``output`` values in the traceback.
|
||||||
|
@ -662,11 +665,11 @@ This means that we only run two tests if no option is passed::
|
||||||
$ py.test -q test_compute.py
|
$ py.test -q test_compute.py
|
||||||
collecting ... collected 2 items
|
collecting ... collected 2 items
|
||||||
..
|
..
|
||||||
2 passed in 0.02 seconds
|
2 passed in 0.01 seconds
|
||||||
|
|
||||||
And we run five tests if we add the ``--all`` option::
|
And we run five tests if we add the ``--all`` option::
|
||||||
|
|
||||||
$ py.test -q --all
|
$ py.test -q --all test_compute.py
|
||||||
collecting ... collected 5 items
|
collecting ... collected 5 items
|
||||||
....F
|
....F
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
|
@ -679,7 +682,7 @@ And we run five tests if we add the ``--all`` option::
|
||||||
E assert 4 < 4
|
E assert 4 < 4
|
||||||
|
|
||||||
test_compute.py:3: AssertionError
|
test_compute.py:3: AssertionError
|
||||||
1 failed, 4 passed in 0.03 seconds
|
1 failed, 4 passed in 0.02 seconds
|
||||||
|
|
||||||
As expected when running the full range of ``param1`` values
|
As expected when running the full range of ``param1`` values
|
||||||
we'll get an error on the last one.
|
we'll get an error on the last one.
|
||||||
|
@ -748,13 +751,4 @@ the mechanism was extended and refined:
|
||||||
use remains fully supported and existing code using it should run
|
use remains fully supported and existing code using it should run
|
||||||
unmodified.
|
unmodified.
|
||||||
|
|
||||||
.. _request:
|
|
||||||
|
|
||||||
The request object passed to old-style factories
|
|
||||||
-----------------------------------------------------------------
|
|
||||||
|
|
||||||
Old-style funcarg factory definitions can receive a :py:class:`~_pytest.python.FuncargRequest` object which
|
|
||||||
provides methods to manage caching and finalization in the context of the
|
|
||||||
test invocation as well as several attributes of the the underlying test item.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -331,9 +331,6 @@ test execution:
|
||||||
Reference of objects involved in hooks
|
Reference of objects involved in hooks
|
||||||
===========================================================
|
===========================================================
|
||||||
|
|
||||||
.. autoclass:: _pytest.python.FuncargRequest()
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: _pytest.config.Config()
|
.. autoclass:: _pytest.config.Config()
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ you can do with the old-style and much more. Specifically setup functions:
|
||||||
- fully interoperate with parametrized resources,
|
- fully interoperate with parametrized resources,
|
||||||
- can be defined in a plugin or :ref:`conftest.py <conftest.py>` file and get called
|
- can be defined in a plugin or :ref:`conftest.py <conftest.py>` file and get called
|
||||||
on a per-session, per-module, per-class or per-function basis,
|
on a per-session, per-module, per-class or per-function basis,
|
||||||
- can access the :ref:`testcontext <testcontext>` for which the setup is called,
|
- can access the :ref:`request <request>` for which the setup is called,
|
||||||
- can precisely control teardown by registering one or multiple
|
- can precisely control teardown by registering one or multiple
|
||||||
teardown functions as soon as they have performed some actions
|
teardown functions as soon as they have performed some actions
|
||||||
which need undoing, eliminating the no need for a separate
|
which need undoing, eliminating the no need for a separate
|
||||||
|
@ -140,8 +140,8 @@ a per-module setup function. We use a :ref:`resource factory
|
||||||
return GlobalResource()
|
return GlobalResource()
|
||||||
|
|
||||||
@pytest.setup(scope="module")
|
@pytest.setup(scope="module")
|
||||||
def setresource(testcontext, globresource):
|
def setresource(request, globresource):
|
||||||
testcontext.module.globresource = globresource
|
request.module.globresource = globresource
|
||||||
|
|
||||||
Now any test module can access ``globresource`` as a module global::
|
Now any test module can access ``globresource`` as a module global::
|
||||||
|
|
||||||
|
@ -178,16 +178,16 @@ factory and also add a finalizer::
|
||||||
self.param = param
|
self.param = param
|
||||||
|
|
||||||
@pytest.factory(scope="session", params=[1,2])
|
@pytest.factory(scope="session", params=[1,2])
|
||||||
def globresource(testcontext):
|
def globresource(request):
|
||||||
g = GlobalResource(testcontext.param)
|
g = GlobalResource(request.param)
|
||||||
def fin():
|
def fin():
|
||||||
print "finalizing", g
|
print "finalizing", g
|
||||||
testcontext.addfinalizer(fin)
|
request.addfinalizer(fin)
|
||||||
return g
|
return g
|
||||||
|
|
||||||
@pytest.setup(scope="module")
|
@pytest.setup(scope="module")
|
||||||
def setresource(testcontext, globresource):
|
def setresource(request, globresource):
|
||||||
testcontext.module.globresource = globresource
|
request.module.globresource = globresource
|
||||||
|
|
||||||
And then re-run our test module::
|
And then re-run our test module::
|
||||||
|
|
||||||
|
|
|
@ -1590,8 +1590,8 @@ class TestRequestAPI:
|
||||||
result = testdir.makeconftest("""
|
result = testdir.makeconftest("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.setup()
|
@pytest.setup()
|
||||||
def mysetup(testcontext):
|
def mysetup(request):
|
||||||
testcontext.uses_funcarg("db")
|
request.uses_funcarg("db")
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
@ -1647,9 +1647,9 @@ class TestFuncargFactory:
|
||||||
import pytest
|
import pytest
|
||||||
l = []
|
l = []
|
||||||
@pytest.factory(params=[1,2])
|
@pytest.factory(params=[1,2])
|
||||||
def arg1(testcontext):
|
def arg1(request):
|
||||||
l.append(1)
|
l.append(1)
|
||||||
return testcontext.param
|
return request.param
|
||||||
|
|
||||||
@pytest.factory()
|
@pytest.factory()
|
||||||
def arg2(arg1):
|
def arg2(arg1):
|
||||||
|
@ -1758,7 +1758,7 @@ class TestSetupDiscovery:
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.setup()
|
@pytest.setup()
|
||||||
def perfunction(testcontext, tmpdir):
|
def perfunction(request, tmpdir):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@pytest.factory()
|
@pytest.factory()
|
||||||
|
@ -1782,10 +1782,10 @@ class TestSetupDiscovery:
|
||||||
setupcalls, allnames = fm.getsetuplist(item.nodeid)
|
setupcalls, allnames = fm.getsetuplist(item.nodeid)
|
||||||
assert len(setupcalls) == 2
|
assert len(setupcalls) == 2
|
||||||
assert setupcalls[0].func.__name__ == "perfunction"
|
assert setupcalls[0].func.__name__ == "perfunction"
|
||||||
assert "testcontext" in setupcalls[0].funcargnames
|
assert "request" in setupcalls[0].funcargnames
|
||||||
assert "tmpdir" in setupcalls[0].funcargnames
|
assert "tmpdir" in setupcalls[0].funcargnames
|
||||||
assert setupcalls[1].func.__name__ == "perfunction2"
|
assert setupcalls[1].func.__name__ == "perfunction2"
|
||||||
assert "testcontext" not in setupcalls[1].funcargnames
|
assert "request" not in setupcalls[1].funcargnames
|
||||||
assert "arg1" in setupcalls[1].funcargnames
|
assert "arg1" in setupcalls[1].funcargnames
|
||||||
assert "tmpdir" not in setupcalls[1].funcargnames
|
assert "tmpdir" not in setupcalls[1].funcargnames
|
||||||
#assert "tmpdir" in setupcalls[1].depfuncargs
|
#assert "tmpdir" in setupcalls[1].depfuncargs
|
||||||
|
@ -1842,8 +1842,8 @@ class TestSetupManagement:
|
||||||
import pytest
|
import pytest
|
||||||
l = []
|
l = []
|
||||||
@pytest.factory(params=[1,2])
|
@pytest.factory(params=[1,2])
|
||||||
def arg(testcontext):
|
def arg(request):
|
||||||
return testcontext.param
|
return request.param
|
||||||
|
|
||||||
@pytest.setup()
|
@pytest.setup()
|
||||||
def something(arg):
|
def something(arg):
|
||||||
|
@ -1868,12 +1868,12 @@ class TestSetupManagement:
|
||||||
l = []
|
l = []
|
||||||
|
|
||||||
@pytest.factory(scope="session", params=[1,2])
|
@pytest.factory(scope="session", params=[1,2])
|
||||||
def arg(testcontext):
|
def arg(request):
|
||||||
return testcontext.param
|
return request.param
|
||||||
|
|
||||||
@pytest.setup(scope="function")
|
@pytest.setup(scope="function")
|
||||||
def append(testcontext, arg):
|
def append(request, arg):
|
||||||
if testcontext.function.__name__ == "test_some":
|
if request.function.__name__ == "test_some":
|
||||||
l.append(arg)
|
l.append(arg)
|
||||||
|
|
||||||
def test_some():
|
def test_some():
|
||||||
|
@ -1894,18 +1894,18 @@ class TestSetupManagement:
|
||||||
l = []
|
l = []
|
||||||
|
|
||||||
@pytest.factory(scope="function", params=[1,2])
|
@pytest.factory(scope="function", params=[1,2])
|
||||||
def farg(testcontext):
|
def farg(request):
|
||||||
return testcontext.param
|
return request.param
|
||||||
|
|
||||||
@pytest.factory(scope="class", params=list("ab"))
|
@pytest.factory(scope="class", params=list("ab"))
|
||||||
def carg(testcontext):
|
def carg(request):
|
||||||
return testcontext.param
|
return request.param
|
||||||
|
|
||||||
@pytest.setup(scope="class")
|
@pytest.setup(scope="function")
|
||||||
def append(testcontext, farg, carg):
|
def append(request, farg, carg):
|
||||||
def fin():
|
def fin():
|
||||||
l.append("fin_%s%s" % (carg, farg))
|
l.append("fin_%s%s" % (carg, farg))
|
||||||
testcontext.addfinalizer(fin)
|
request.addfinalizer(fin)
|
||||||
""")
|
""")
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -1950,8 +1950,8 @@ class TestFuncargMarker:
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.factory(params=["a", "b", "c"])
|
@pytest.factory(params=["a", "b", "c"])
|
||||||
def arg(testcontext):
|
def arg(request):
|
||||||
return testcontext.param
|
return request.param
|
||||||
l = []
|
l = []
|
||||||
def test_param(arg):
|
def test_param(arg):
|
||||||
l.append(arg)
|
l.append(arg)
|
||||||
|
@ -2011,10 +2011,10 @@ class TestFuncargMarker:
|
||||||
finalized = []
|
finalized = []
|
||||||
created = []
|
created = []
|
||||||
@pytest.factory(scope="module")
|
@pytest.factory(scope="module")
|
||||||
def arg(testcontext):
|
def arg(request):
|
||||||
created.append(1)
|
created.append(1)
|
||||||
assert testcontext.scope == "module"
|
assert request.scope == "module"
|
||||||
testcontext.addfinalizer(lambda: finalized.append(1))
|
request.addfinalizer(lambda: finalized.append(1))
|
||||||
def pytest_funcarg__created(request):
|
def pytest_funcarg__created(request):
|
||||||
return len(created)
|
return len(created)
|
||||||
def pytest_funcarg__finalized(request):
|
def pytest_funcarg__finalized(request):
|
||||||
|
@ -2050,14 +2050,14 @@ class TestFuncargMarker:
|
||||||
finalized = []
|
finalized = []
|
||||||
created = []
|
created = []
|
||||||
@pytest.factory(scope="function")
|
@pytest.factory(scope="function")
|
||||||
def arg(testcontext):
|
def arg(request):
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
test_mod1="""
|
test_mod1="""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.factory(scope="session")
|
@pytest.factory(scope="session")
|
||||||
def arg(testcontext):
|
def arg(request):
|
||||||
%s
|
%s
|
||||||
def test_1(arg):
|
def test_1(arg):
|
||||||
pass
|
pass
|
||||||
|
@ -2091,8 +2091,8 @@ class TestFuncargMarker:
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.factory(scope="module", params=["a", "b", "c"])
|
@pytest.factory(scope="module", params=["a", "b", "c"])
|
||||||
def arg(testcontext):
|
def arg(request):
|
||||||
return testcontext.param
|
return request.param
|
||||||
l = []
|
l = []
|
||||||
def test_param(arg):
|
def test_param(arg):
|
||||||
l.append(arg)
|
l.append(arg)
|
||||||
|
@ -2109,7 +2109,7 @@ class TestFuncargMarker:
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.factory(scope="function")
|
@pytest.factory(scope="function")
|
||||||
def arg(testcontext):
|
def arg(request):
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
|
@ -2131,8 +2131,8 @@ class TestFuncargMarker:
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.factory(scope="module", params=[1, 2])
|
@pytest.factory(scope="module", params=[1, 2])
|
||||||
def arg(testcontext):
|
def arg(request):
|
||||||
return testcontext.param
|
return request.param
|
||||||
|
|
||||||
l = []
|
l = []
|
||||||
def test_1(arg):
|
def test_1(arg):
|
||||||
|
@ -2198,18 +2198,18 @@ class TestFuncargMarker:
|
||||||
l = []
|
l = []
|
||||||
|
|
||||||
@pytest.factory(scope="function", params=[1,2])
|
@pytest.factory(scope="function", params=[1,2])
|
||||||
def farg(testcontext):
|
def farg(request):
|
||||||
return testcontext.param
|
return request.param
|
||||||
|
|
||||||
@pytest.factory(scope="class", params=list("ab"))
|
@pytest.factory(scope="class", params=list("ab"))
|
||||||
def carg(testcontext):
|
def carg(request):
|
||||||
return testcontext.param
|
return request.param
|
||||||
|
|
||||||
@pytest.setup(scope="class")
|
@pytest.setup(scope="function")
|
||||||
def append(testcontext, farg, carg):
|
def append(request, farg, carg):
|
||||||
def fin():
|
def fin():
|
||||||
l.append("fin_%s%s" % (carg, farg))
|
l.append("fin_%s%s" % (carg, farg))
|
||||||
testcontext.addfinalizer(fin)
|
request.addfinalizer(fin)
|
||||||
""")
|
""")
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -2244,18 +2244,18 @@ class TestFuncargMarker:
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.factory(scope="function", params=[1, 2])
|
@pytest.factory(scope="function", params=[1, 2])
|
||||||
def arg(testcontext):
|
def arg(request):
|
||||||
param = testcontext.param
|
param = request.param
|
||||||
testcontext.addfinalizer(lambda: l.append("fin:%s" % param))
|
request.addfinalizer(lambda: l.append("fin:%s" % param))
|
||||||
l.append("create:%s" % param)
|
l.append("create:%s" % param)
|
||||||
return testcontext.param
|
return request.param
|
||||||
|
|
||||||
@pytest.factory(scope="module", params=["mod1", "mod2"])
|
@pytest.factory(scope="module", params=["mod1", "mod2"])
|
||||||
def modarg(testcontext):
|
def modarg(request):
|
||||||
param = testcontext.param
|
param = request.param
|
||||||
testcontext.addfinalizer(lambda: l.append("fin:%s" % param))
|
request.addfinalizer(lambda: l.append("fin:%s" % param))
|
||||||
l.append("create:%s" % param)
|
l.append("create:%s" % param)
|
||||||
return testcontext.param
|
return request.param
|
||||||
|
|
||||||
l = []
|
l = []
|
||||||
def test_1(arg):
|
def test_1(arg):
|
||||||
|
@ -2288,11 +2288,11 @@ class TestFuncargMarker:
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.factory(scope="module", params=[1, 2])
|
@pytest.factory(scope="module", params=[1, 2])
|
||||||
def arg(testcontext):
|
def arg(request):
|
||||||
testcontext.config.l = l # to access from outer
|
request.config.l = l # to access from outer
|
||||||
x = testcontext.param
|
x = request.param
|
||||||
testcontext.addfinalizer(lambda: l.append("fin%s" % x))
|
request.addfinalizer(lambda: l.append("fin%s" % x))
|
||||||
return testcontext.param
|
return request.param
|
||||||
|
|
||||||
l = []
|
l = []
|
||||||
def test_1(arg):
|
def test_1(arg):
|
||||||
|
@ -2317,10 +2317,10 @@ class TestFuncargMarker:
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.factory(scope="function", params=[1, 2])
|
@pytest.factory(scope="function", params=[1, 2])
|
||||||
def arg(testcontext):
|
def arg(request):
|
||||||
x = testcontext.param
|
x = request.param
|
||||||
testcontext.addfinalizer(lambda: l.append("fin%s" % x))
|
request.addfinalizer(lambda: l.append("fin%s" % x))
|
||||||
return testcontext.param
|
return request.param
|
||||||
|
|
||||||
l = []
|
l = []
|
||||||
def test_1(arg):
|
def test_1(arg):
|
||||||
|
@ -2339,12 +2339,12 @@ class TestFuncargMarker:
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.factory(scope="module", params=[1, 2])
|
@pytest.factory(scope="module", params=[1, 2])
|
||||||
def arg(testcontext):
|
def arg(request):
|
||||||
return testcontext.param
|
return request.param
|
||||||
|
|
||||||
@pytest.setup(scope="module")
|
@pytest.setup(scope="module")
|
||||||
def mysetup(testcontext, arg):
|
def mysetup(request, arg):
|
||||||
testcontext.addfinalizer(lambda: l.append("fin%s" % arg))
|
request.addfinalizer(lambda: l.append("fin%s" % arg))
|
||||||
l.append("setup%s" % arg)
|
l.append("setup%s" % arg)
|
||||||
|
|
||||||
l = []
|
l = []
|
||||||
|
@ -2376,32 +2376,32 @@ class TestTestContextScopeAccess:
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.setup(scope=%r)
|
@pytest.setup(scope=%r)
|
||||||
def myscoped(testcontext):
|
def myscoped(request):
|
||||||
for x in %r:
|
for x in %r:
|
||||||
assert hasattr(testcontext, x)
|
assert hasattr(request, x)
|
||||||
for x in %r:
|
for x in %r:
|
||||||
pytest.raises(AttributeError, lambda:
|
pytest.raises(AttributeError, lambda:
|
||||||
getattr(testcontext, x))
|
getattr(request, x))
|
||||||
assert testcontext.session
|
assert request.session
|
||||||
assert testcontext.config
|
assert request.config
|
||||||
def test_func():
|
def test_func():
|
||||||
pass
|
pass
|
||||||
""" %(scope, ok.split(), error.split()))
|
""" %(scope, ok.split(), error.split()))
|
||||||
reprec = testdir.inline_run()
|
reprec = testdir.inline_run("-l")
|
||||||
reprec.assertoutcome(passed=1)
|
reprec.assertoutcome(passed=1)
|
||||||
|
|
||||||
def test_funcarg(self, testdir, scope, ok, error):
|
def test_funcarg(self, testdir, scope, ok, error):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.factory(scope=%r)
|
@pytest.factory(scope=%r)
|
||||||
def arg(testcontext):
|
def arg(request):
|
||||||
for x in %r:
|
for x in %r:
|
||||||
assert hasattr(testcontext, x)
|
assert hasattr(request, x)
|
||||||
for x in %r:
|
for x in %r:
|
||||||
pytest.raises(AttributeError, lambda:
|
pytest.raises(AttributeError, lambda:
|
||||||
getattr(testcontext, x))
|
getattr(request, x))
|
||||||
assert testcontext.session
|
assert request.session
|
||||||
assert testcontext.config
|
assert request.config
|
||||||
def test_func(arg):
|
def test_func(arg):
|
||||||
pass
|
pass
|
||||||
""" %(scope, ok.split(), error.split()))
|
""" %(scope, ok.split(), error.split()))
|
||||||
|
@ -2414,7 +2414,7 @@ class TestErrors:
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.factory()
|
@pytest.factory()
|
||||||
def gen(request):
|
def gen(qwe123):
|
||||||
return 1
|
return 1
|
||||||
def test_something(gen):
|
def test_something(gen):
|
||||||
pass
|
pass
|
||||||
|
@ -2422,8 +2422,8 @@ class TestErrors:
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
assert result.ret != 0
|
assert result.ret != 0
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*def gen(request):*",
|
"*def gen(qwe123):*",
|
||||||
"*no factory*request*",
|
"*no factory*qwe123*",
|
||||||
"*1 error*",
|
"*1 error*",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -2431,7 +2431,7 @@ class TestErrors:
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.setup()
|
@pytest.setup()
|
||||||
def gen(request):
|
def gen(qwe123):
|
||||||
return 1
|
return 1
|
||||||
def test_something():
|
def test_something():
|
||||||
pass
|
pass
|
||||||
|
@ -2439,14 +2439,14 @@ class TestErrors:
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
assert result.ret != 0
|
assert result.ret != 0
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*def gen(request):*",
|
"*def gen(qwe123):*",
|
||||||
"*no factory*request*",
|
"*no factory*qwe123*",
|
||||||
"*1 error*",
|
"*1 error*",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
class TestTestContextVarious:
|
class TestTestContextVarious:
|
||||||
def test_newstyle_no_request(self, testdir):
|
def test_newstyle_with_request(self, testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.factory()
|
@pytest.factory()
|
||||||
|
@ -2455,21 +2455,19 @@ class TestTestContextVarious:
|
||||||
def test_1(arg):
|
def test_1(arg):
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest()
|
reprec = testdir.inline_run()
|
||||||
result.stdout.fnmatch_lines([
|
reprec.assertoutcome(passed=1)
|
||||||
"*no factory found*request*",
|
|
||||||
])
|
|
||||||
|
|
||||||
def test_setupcontext_no_param(self, testdir):
|
def test_setupcontext_no_param(self, testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.factory(params=[1,2])
|
@pytest.factory(params=[1,2])
|
||||||
def arg(testcontext):
|
def arg(request):
|
||||||
return testcontext.param
|
return request.param
|
||||||
|
|
||||||
@pytest.setup()
|
@pytest.setup()
|
||||||
def mysetup(testcontext, arg):
|
def mysetup(request, arg):
|
||||||
assert not hasattr(testcontext, "param")
|
assert not hasattr(request, "param")
|
||||||
def test_1(arg):
|
def test_1(arg):
|
||||||
assert arg in (1,2)
|
assert arg in (1,2)
|
||||||
""")
|
""")
|
||||||
|
@ -2505,3 +2503,16 @@ def test_setupdecorator_and_xunit(testdir):
|
||||||
""")
|
""")
|
||||||
reprec = testdir.inline_run()
|
reprec = testdir.inline_run()
|
||||||
reprec.assertoutcome(passed=3)
|
reprec.assertoutcome(passed=3)
|
||||||
|
|
||||||
|
def test_request_can_be_overridden(testdir):
|
||||||
|
testdir.makepyfile("""
|
||||||
|
import pytest
|
||||||
|
@pytest.factory()
|
||||||
|
def request(request):
|
||||||
|
request.a = 1
|
||||||
|
return request
|
||||||
|
def test_request(request):
|
||||||
|
assert request.a == 1
|
||||||
|
""")
|
||||||
|
reprec = testdir.inline_run()
|
||||||
|
reprec.assertoutcome(passed=1)
|
||||||
|
|
Loading…
Reference in New Issue