implement funcargs according to docs, streamline docs
--HG-- branch : trunk
This commit is contained in:
parent
286460b94e
commit
1e3acc66d6
|
@ -11,7 +11,7 @@ of making it easy to:
|
||||||
* manage test value setup and teardown depending on
|
* manage test value setup and teardown depending on
|
||||||
command line options or configuration
|
command line options or configuration
|
||||||
* parametrize multiple runs of the same test functions
|
* parametrize multiple runs of the same test functions
|
||||||
* present useful debug info if something goes wrong
|
* present useful debug info if setup goes wrong
|
||||||
|
|
||||||
Using funcargs, test functions become more expressive,
|
Using funcargs, test functions become more expressive,
|
||||||
more "templaty" and more test-aspect oriented. In fact,
|
more "templaty" and more test-aspect oriented. In fact,
|
||||||
|
@ -35,8 +35,8 @@ funcarg providers: setting up test function arguments
|
||||||
|
|
||||||
Test functions can specify one ore more arguments ("funcargs")
|
Test functions can specify one ore more arguments ("funcargs")
|
||||||
and a test module or plugin can define functions that provide
|
and a test module or plugin can define functions that provide
|
||||||
the function argument. Let's look at a self-contained example
|
the function argument. Let's look at a simple self-contained
|
||||||
that you can put into a test module:
|
example that you can put into a test module:
|
||||||
|
|
||||||
.. sourcecode:: python
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
@ -48,28 +48,30 @@ that you can put into a test module:
|
||||||
|
|
||||||
Here is what happens:
|
Here is what happens:
|
||||||
|
|
||||||
1. **lookup funcarg provider**: The ``test_function`` needs an value for
|
1. **lookup funcarg provider**: For executing ``test_function(myfuncarg)``
|
||||||
``myfuncarg`` to run. The provider is found by its special
|
a value is needed. A value provider is found by looking for a
|
||||||
name, ``pytest_funcarg__`` followed by the function
|
function with a special name of ``pytest_funcarg__${ARGNAME}``.
|
||||||
argument argument name. If a provider cannot be found,
|
|
||||||
a list of all available function arguments is presented.
|
|
||||||
|
|
||||||
2. **setup funcarg value**: ``pytest_funcarg__myfuncarg(request)`` is
|
2. **setup funcarg value**: ``pytest_funcarg__myfuncarg(request)`` is
|
||||||
called to setup the value for ``myfuncarg``.
|
called to setup and return the value for ``myfuncarg``.
|
||||||
|
|
||||||
3. **execute test** ``test_function(42)`` call is executed.
|
3. **execute test** ``test_function(42)`` call is executed.
|
||||||
If the test fails one can see the original provided
|
|
||||||
value in the traceback at the top.
|
Note that if a provider cannot be found a list of
|
||||||
|
available function arguments will be provided.
|
||||||
|
|
||||||
|
For providers that makes use of the `request object`_
|
||||||
|
please look into the `tutorial examples`_.
|
||||||
|
|
||||||
.. _`request object`:
|
.. _`request object`:
|
||||||
|
|
||||||
funcarg request objects
|
funcarg request objects
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
Request objects are passed to funcarg providers. Request objects
|
Request objects are passed to funcarg providers. They
|
||||||
encapsulate a request for a function argument for a
|
encapsulate a request for a function argument for a
|
||||||
specific test function. Request objects allow providers to access
|
specific test function. Request objects allow providers
|
||||||
test configuration and test context:
|
to access test configuration and test context:
|
||||||
|
|
||||||
``request.argname``: name of the requested function argument
|
``request.argname``: name of the requested function argument
|
||||||
|
|
||||||
|
@ -81,7 +83,7 @@ test configuration and test context:
|
||||||
|
|
||||||
``request.config``: access to command line opts and general config
|
``request.config``: access to command line opts and general config
|
||||||
|
|
||||||
``request.param``: if exists is the argument passed by a `parametrizing test generator`_
|
``request.param``: if exists was passed by a `parametrizing test generator`_
|
||||||
|
|
||||||
|
|
||||||
cleanup after test function execution
|
cleanup after test function execution
|
||||||
|
@ -90,9 +92,9 @@ cleanup after test function execution
|
||||||
Request objects allow to **register a finalizer method** which is
|
Request objects allow to **register a finalizer method** which is
|
||||||
called after a test function has finished running.
|
called after a test function has finished running.
|
||||||
This is useful for tearing down or cleaning up
|
This is useful for tearing down or cleaning up
|
||||||
test state. Here is a basic example for providing
|
test state related to a function argument. Here is a basic
|
||||||
a ``myfile`` object that will be closed upon test
|
example for providing a ``myfile`` object that will be
|
||||||
function finish:
|
closed upon test function finish:
|
||||||
|
|
||||||
.. sourcecode:: python
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
@ -152,7 +154,7 @@ Here is what happens in detail:
|
||||||
function. The `metafunc object`_ has context information.
|
function. The `metafunc object`_ has context information.
|
||||||
``metafunc.addcall(param=i)`` schedules a new test call
|
``metafunc.addcall(param=i)`` schedules a new test call
|
||||||
such that function argument providers will see an additional
|
such that function argument providers will see an additional
|
||||||
``arg`` attribute on their request object.
|
``param`` attribute on their request object.
|
||||||
|
|
||||||
2. **setup funcarg values**: the ``pytest_funcarg__arg1(request)`` provider is called
|
2. **setup funcarg values**: the ``pytest_funcarg__arg1(request)`` provider is called
|
||||||
10 times with ten different request objects all pointing to
|
10 times with ten different request objects all pointing to
|
||||||
|
@ -208,6 +210,8 @@ even happen in a different process. Therefore one should
|
||||||
defer setup of heavyweight objects to funcarg providers.*
|
defer setup of heavyweight objects to funcarg providers.*
|
||||||
|
|
||||||
|
|
||||||
|
.. _`tutorial examples`:
|
||||||
|
|
||||||
Funcarg Tutorial Examples
|
Funcarg Tutorial Examples
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
|
@ -291,13 +295,7 @@ local plugin that adds a command line option to ``py.test`` invocations:
|
||||||
|
|
||||||
.. sourcecode:: python
|
.. sourcecode:: python
|
||||||
|
|
||||||
class ConftestPlugin:
|
# ./conftest.py
|
||||||
def pytest_addoption(self, parser):
|
|
||||||
parser.addoption("--ssh", action="store", default=None,
|
|
||||||
help="specify ssh host to run tests with")
|
|
||||||
|
|
||||||
pytest_funcarg__mysetup = MySetupFuncarg
|
|
||||||
|
|
||||||
class MySetupFuncarg:
|
class MySetupFuncarg:
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
self.request = request
|
self.request = request
|
||||||
|
@ -307,10 +305,19 @@ local plugin that adds a command line option to ``py.test`` invocations:
|
||||||
py.test.skip("specify ssh host with --ssh to run this test")
|
py.test.skip("specify ssh host with --ssh to run this test")
|
||||||
return py.execnet.SshGateway(host)
|
return py.execnet.SshGateway(host)
|
||||||
|
|
||||||
|
class ConftestPlugin:
|
||||||
|
def pytest_addoption(self, parser):
|
||||||
|
parser.addoption("--ssh", action="store", default=None,
|
||||||
|
help="specify ssh host to run tests with")
|
||||||
|
|
||||||
|
# alias the above class as the "mysetup" provider
|
||||||
|
pytest_funcarg__mysetup = MySetupFuncarg
|
||||||
|
|
||||||
Now any test functions can use the ``mysetup.getsshconnection()`` method like this:
|
Now any test functions can use the ``mysetup.getsshconnection()`` method like this:
|
||||||
|
|
||||||
.. sourcecode:: python
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
# ./test_function.py
|
||||||
class TestClass:
|
class TestClass:
|
||||||
def test_function(self, mysetup):
|
def test_function(self, mysetup):
|
||||||
conn = mysetup.getsshconnection()
|
conn = mysetup.getsshconnection()
|
||||||
|
@ -326,6 +333,7 @@ example: specifying and selecting acceptance tests
|
||||||
|
|
||||||
.. sourcecode:: python
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
# ./conftest.py
|
||||||
class ConftestPlugin:
|
class ConftestPlugin:
|
||||||
def pytest_option(self, parser):
|
def pytest_option(self, parser):
|
||||||
group = parser.getgroup("myproject")
|
group = parser.getgroup("myproject")
|
||||||
|
|
|
@ -10,14 +10,10 @@ def getfuncargnames(function):
|
||||||
|
|
||||||
def fillfuncargs(function):
|
def fillfuncargs(function):
|
||||||
""" fill missing funcargs. """
|
""" fill missing funcargs. """
|
||||||
if function._args:
|
argnames = getfuncargnames(function.obj)
|
||||||
# functions yielded from a generator: we don't want
|
if argnames:
|
||||||
# to support that because we want to go here anyway:
|
assert not function._args, "yielded functions cannot have funcargs"
|
||||||
# http://bitbucket.org/hpk42/py-trunk/issue/2/next-generation-generative-tests
|
for argname in argnames:
|
||||||
pass
|
|
||||||
else:
|
|
||||||
# standard Python Test function/method case
|
|
||||||
for argname in getfuncargnames(function.obj):
|
|
||||||
if argname not in function.funcargs:
|
if argname not in function.funcargs:
|
||||||
request = FuncargRequest(pyfuncitem=function, argname=argname)
|
request = FuncargRequest(pyfuncitem=function, argname=argname)
|
||||||
try:
|
try:
|
||||||
|
@ -25,12 +21,15 @@ def fillfuncargs(function):
|
||||||
except request.Error:
|
except request.Error:
|
||||||
request._raiselookupfailed()
|
request._raiselookupfailed()
|
||||||
|
|
||||||
class CallSpec:
|
|
||||||
def __init__(self, id, funcargs):
|
|
||||||
self.id = id
|
|
||||||
self.funcargs = funcargs
|
|
||||||
|
|
||||||
class FuncSpecs:
|
_notexists = object()
|
||||||
|
class CallSpec:
|
||||||
|
def __init__(self, id, param):
|
||||||
|
self.id = id
|
||||||
|
if param is not _notexists:
|
||||||
|
self.param = param
|
||||||
|
|
||||||
|
class Metafunc:
|
||||||
def __init__(self, function, config=None, cls=None, module=None):
|
def __init__(self, function, config=None, cls=None, module=None):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.module = module
|
self.module = module
|
||||||
|
@ -41,20 +40,14 @@ class FuncSpecs:
|
||||||
self._calls = []
|
self._calls = []
|
||||||
self._ids = py.builtin.set()
|
self._ids = py.builtin.set()
|
||||||
|
|
||||||
def addcall(self, _id=None, **kwargs):
|
def addcall(self, id=None, param=_notexists):
|
||||||
for argname in kwargs:
|
if id is None:
|
||||||
if argname[0] == "_":
|
id = len(self._calls)
|
||||||
raise TypeError("argument %r is not a valid keyword." % argname)
|
id = str(id)
|
||||||
if argname not in self.funcargnames:
|
if id in self._ids:
|
||||||
raise ValueError("function %r has no funcarg %r" %(
|
raise ValueError("duplicate id %r" % id)
|
||||||
self.function, argname))
|
self._ids.add(id)
|
||||||
if _id is None:
|
self._calls.append(CallSpec(id, param))
|
||||||
_id = len(self._calls)
|
|
||||||
_id = str(_id)
|
|
||||||
if _id in self._ids:
|
|
||||||
raise ValueError("duplicate id %r" % _id)
|
|
||||||
self._ids.add(_id)
|
|
||||||
self._calls.append(CallSpec(_id, kwargs))
|
|
||||||
|
|
||||||
class FunctionCollector(py.test.collect.Collector):
|
class FunctionCollector(py.test.collect.Collector):
|
||||||
def __init__(self, name, parent, calls):
|
def __init__(self, name, parent, calls):
|
||||||
|
@ -66,7 +59,7 @@ class FunctionCollector(py.test.collect.Collector):
|
||||||
l = []
|
l = []
|
||||||
for call in self.calls:
|
for call in self.calls:
|
||||||
function = self.parent.Function(name="%s[%s]" %(self.name, call.id),
|
function = self.parent.Function(name="%s[%s]" %(self.name, call.id),
|
||||||
parent=self, funcargs=call.funcargs, callobj=self.obj)
|
parent=self, requestparam=call.param, callobj=self.obj)
|
||||||
l.append(function)
|
l.append(function)
|
||||||
return l
|
return l
|
||||||
|
|
||||||
|
@ -75,7 +68,7 @@ class FuncargRequest:
|
||||||
|
|
||||||
class Error(LookupError):
|
class Error(LookupError):
|
||||||
""" error on performing funcarg request. """
|
""" error on performing funcarg request. """
|
||||||
|
|
||||||
def __init__(self, pyfuncitem, argname):
|
def __init__(self, pyfuncitem, argname):
|
||||||
self._pyfuncitem = pyfuncitem
|
self._pyfuncitem = pyfuncitem
|
||||||
self.argname = argname
|
self.argname = argname
|
||||||
|
@ -84,6 +77,8 @@ class FuncargRequest:
|
||||||
self.cls = getattr(self.function, 'im_class', None)
|
self.cls = getattr(self.function, 'im_class', None)
|
||||||
self.config = pyfuncitem.config
|
self.config = pyfuncitem.config
|
||||||
self.fspath = pyfuncitem.fspath
|
self.fspath = pyfuncitem.fspath
|
||||||
|
if hasattr(pyfuncitem, '_requestparam'):
|
||||||
|
self.param = pyfuncitem._requestparam
|
||||||
self._plugins = self.config.pluginmanager.getplugins()
|
self._plugins = self.config.pluginmanager.getplugins()
|
||||||
self._plugins.append(self.module)
|
self._plugins.append(self.module)
|
||||||
self._provider = self.config.pluginmanager.listattr(
|
self._provider = self.config.pluginmanager.listattr(
|
||||||
|
@ -91,9 +86,6 @@ class FuncargRequest:
|
||||||
attrname=self._argprefix + str(argname)
|
attrname=self._argprefix + str(argname)
|
||||||
)
|
)
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<FuncargRequest %r for %r>" %(self.argname, self._pyfuncitem)
|
|
||||||
|
|
||||||
def call_next_provider(self):
|
def call_next_provider(self):
|
||||||
if not self._provider:
|
if not self._provider:
|
||||||
raise self.Error("no provider methods left")
|
raise self.Error("no provider methods left")
|
||||||
|
@ -103,6 +95,9 @@ class FuncargRequest:
|
||||||
def addfinalizer(self, finalizer):
|
def addfinalizer(self, finalizer):
|
||||||
self._pyfuncitem.addfinalizer(finalizer)
|
self._pyfuncitem.addfinalizer(finalizer)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<FuncargRequest %r for %r>" %(self.argname, self._pyfuncitem)
|
||||||
|
|
||||||
def _raiselookupfailed(self):
|
def _raiselookupfailed(self):
|
||||||
available = []
|
available = []
|
||||||
for plugin in self._plugins:
|
for plugin in self._plugins:
|
||||||
|
|
|
@ -53,7 +53,7 @@ class PluginHooks:
|
||||||
""" return custom item/collector for a python object in a module, or None. """
|
""" return custom item/collector for a python object in a module, or None. """
|
||||||
pytest_pycollect_obj.firstresult = True
|
pytest_pycollect_obj.firstresult = True
|
||||||
|
|
||||||
def pytest_genfunc(self, funcspec):
|
def pytest_generate_tests(self, metafunc):
|
||||||
""" generate (multiple) parametrized calls to a test function."""
|
""" generate (multiple) parametrized calls to a test function."""
|
||||||
|
|
||||||
def pytest_collectstart(self, collector):
|
def pytest_collectstart(self, collector):
|
||||||
|
|
|
@ -151,13 +151,13 @@ class PyCollectorMixin(PyobjMixin, py.test.collect.Collector):
|
||||||
# to work to get at the class
|
# to work to get at the class
|
||||||
clscol = self._getparent(Class)
|
clscol = self._getparent(Class)
|
||||||
cls = clscol and clscol.obj or None
|
cls = clscol and clscol.obj or None
|
||||||
funcspec = funcargs.FuncSpecs(funcobj, config=self.config, cls=cls, module=module)
|
metafunc = funcargs.Metafunc(funcobj, config=self.config, cls=cls, module=module)
|
||||||
gentesthook = self.config.hook.pytest_genfunc.clone(extralookup=module)
|
gentesthook = self.config.hook.pytest_generate_tests.clone(extralookup=module)
|
||||||
gentesthook(funcspec=funcspec)
|
gentesthook(metafunc=metafunc)
|
||||||
if not funcspec._calls:
|
if not metafunc._calls:
|
||||||
return self.Function(name, parent=self)
|
return self.Function(name, parent=self)
|
||||||
return funcargs.FunctionCollector(name=name,
|
return funcargs.FunctionCollector(name=name,
|
||||||
parent=self, calls=funcspec._calls)
|
parent=self, calls=metafunc._calls)
|
||||||
|
|
||||||
class Module(py.test.collect.File, PyCollectorMixin):
|
class Module(py.test.collect.File, PyCollectorMixin):
|
||||||
def _getobj(self):
|
def _getobj(self):
|
||||||
|
@ -325,13 +325,15 @@ class Function(FunctionMixin, py.test.collect.Item):
|
||||||
""" a Function Item is responsible for setting up
|
""" a Function Item is responsible for setting up
|
||||||
and executing a Python callable test object.
|
and executing a Python callable test object.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, parent=None, config=None, args=(), funcargs=None, callobj=_dummy):
|
def __init__(self, name, parent=None, config=None, args=(),
|
||||||
|
requestparam=_dummy, callobj=_dummy):
|
||||||
super(Function, self).__init__(name, parent, config=config)
|
super(Function, self).__init__(name, parent, config=config)
|
||||||
self._finalizers = []
|
self._finalizers = []
|
||||||
self._args = args
|
self._args = args
|
||||||
if funcargs is None:
|
if not args: # yielded functions (deprecated) have positional args
|
||||||
funcargs = {}
|
self.funcargs = {}
|
||||||
self.funcargs = funcargs
|
if requestparam is not _dummy:
|
||||||
|
self._requestparam = requestparam
|
||||||
if callobj is not _dummy:
|
if callobj is not _dummy:
|
||||||
self._obj = callobj
|
self._obj = callobj
|
||||||
|
|
||||||
|
@ -352,12 +354,14 @@ class Function(FunctionMixin, py.test.collect.Item):
|
||||||
|
|
||||||
def runtest(self):
|
def runtest(self):
|
||||||
""" execute the given test function. """
|
""" execute the given test function. """
|
||||||
self.config.hook.pytest_pyfunc_call(pyfuncitem=self,
|
kwargs = getattr(self, 'funcargs', {})
|
||||||
args=self._args, kwargs=self.funcargs)
|
self.config.hook.pytest_pyfunc_call(
|
||||||
|
pyfuncitem=self, args=self._args, kwargs=kwargs)
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
super(Function, self).setup()
|
super(Function, self).setup()
|
||||||
funcargs.fillfuncargs(self)
|
if hasattr(self, 'funcargs'):
|
||||||
|
funcargs.fillfuncargs(self)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -131,100 +131,91 @@ class TestRequest:
|
||||||
req = funcargs.FuncargRequest(item, "xxx")
|
req = funcargs.FuncargRequest(item, "xxx")
|
||||||
assert req.fspath == modcol.fspath
|
assert req.fspath == modcol.fspath
|
||||||
|
|
||||||
class TestFuncSpecs:
|
class TestMetafunc:
|
||||||
def test_no_funcargs(self, testdir):
|
def test_no_funcargs(self, testdir):
|
||||||
def function(): pass
|
def function(): pass
|
||||||
funcspec = funcargs.FuncSpecs(function)
|
metafunc = funcargs.Metafunc(function)
|
||||||
assert not funcspec.funcargnames
|
assert not metafunc.funcargnames
|
||||||
|
|
||||||
def test_function_basic(self):
|
def test_function_basic(self):
|
||||||
def func(arg1, arg2="qwe"): pass
|
def func(arg1, arg2="qwe"): pass
|
||||||
funcspec = funcargs.FuncSpecs(func)
|
metafunc = funcargs.Metafunc(func)
|
||||||
assert len(funcspec.funcargnames) == 1
|
assert len(metafunc.funcargnames) == 1
|
||||||
assert 'arg1' in funcspec.funcargnames
|
assert 'arg1' in metafunc.funcargnames
|
||||||
assert funcspec.function is func
|
assert metafunc.function is func
|
||||||
assert funcspec.cls is None
|
assert metafunc.cls is None
|
||||||
|
|
||||||
def test_addcall_with_id(self):
|
def test_addcall_no_args(self):
|
||||||
def func(arg1): pass
|
def func(arg1): pass
|
||||||
funcspec = funcargs.FuncSpecs(func)
|
metafunc = funcargs.Metafunc(func)
|
||||||
py.test.raises(TypeError, """
|
metafunc.addcall()
|
||||||
funcspec.addcall(_xyz=10)
|
assert len(metafunc._calls) == 1
|
||||||
""")
|
call = metafunc._calls[0]
|
||||||
funcspec.addcall(_id="hello", arg1=100)
|
assert call.id == "0"
|
||||||
py.test.raises(ValueError, "funcspec.addcall(_id='hello', arg1=100)")
|
assert not hasattr(call, 'param')
|
||||||
call = funcspec._calls[0]
|
|
||||||
assert call.id == "hello"
|
|
||||||
assert call.funcargs == {'arg1': 100}
|
|
||||||
|
|
||||||
def test_addcall_basic(self):
|
def test_addcall_id(self):
|
||||||
def func(arg1): pass
|
def func(arg1): pass
|
||||||
funcspec = funcargs.FuncSpecs(func)
|
metafunc = funcargs.Metafunc(func)
|
||||||
py.test.raises(ValueError, """
|
metafunc.addcall(id=1)
|
||||||
funcspec.addcall(notexists=100)
|
py.test.raises(ValueError, "metafunc.addcall(id=1)")
|
||||||
""")
|
py.test.raises(ValueError, "metafunc.addcall(id='1')")
|
||||||
funcspec.addcall(arg1=100)
|
metafunc.addcall(id=2)
|
||||||
assert len(funcspec._calls) == 1
|
assert len(metafunc._calls) == 2
|
||||||
assert funcspec._calls[0].funcargs == {'arg1': 100}
|
assert metafunc._calls[0].id == "1"
|
||||||
|
assert metafunc._calls[1].id == "2"
|
||||||
|
|
||||||
def test_addcall_two(self):
|
def test_addcall_param(self):
|
||||||
def func(arg1): pass
|
def func(arg1): pass
|
||||||
funcspec = funcargs.FuncSpecs(func)
|
metafunc = funcargs.Metafunc(func)
|
||||||
funcspec.addcall(arg1=100)
|
class obj: pass
|
||||||
funcspec.addcall(arg1=101)
|
metafunc.addcall(param=obj)
|
||||||
assert len(funcspec._calls) == 2
|
metafunc.addcall(param=obj)
|
||||||
assert funcspec._calls[0].funcargs == {'arg1': 100}
|
metafunc.addcall(param=1)
|
||||||
assert funcspec._calls[1].funcargs == {'arg1': 101}
|
assert len(metafunc._calls) == 3
|
||||||
|
assert metafunc._calls[0].param == obj
|
||||||
|
assert metafunc._calls[1].param == obj
|
||||||
|
assert metafunc._calls[2].param == 1
|
||||||
|
|
||||||
|
|
||||||
class TestGenfuncFunctional:
|
class TestGenfuncFunctional:
|
||||||
def test_attributes(self, testdir):
|
def test_attributes(self, testdir):
|
||||||
p = testdir.makepyfile("""
|
p = testdir.makepyfile("""
|
||||||
|
# assumes that generate/provide runs in the same process
|
||||||
import py
|
import py
|
||||||
def pytest_genfunc(funcspec):
|
def pytest_generate_tests(metafunc):
|
||||||
funcspec.addcall(funcspec=funcspec)
|
metafunc.addcall(param=metafunc)
|
||||||
|
|
||||||
|
def pytest_funcarg__metafunc(request):
|
||||||
|
return request.param
|
||||||
|
|
||||||
|
def test_function(metafunc):
|
||||||
|
assert metafunc.config == py.test.config
|
||||||
|
assert metafunc.module.__name__ == __name__
|
||||||
|
assert metafunc.function == test_function
|
||||||
|
assert metafunc.cls is None
|
||||||
|
|
||||||
def test_function(funcspec):
|
|
||||||
assert funcspec.config == py.test.config
|
|
||||||
assert funcspec.module.__name__ == __name__
|
|
||||||
assert funcspec.function == test_function
|
|
||||||
assert funcspec.cls is None
|
|
||||||
class TestClass:
|
class TestClass:
|
||||||
def test_method(self, funcspec):
|
def test_method(self, metafunc):
|
||||||
assert funcspec.config == py.test.config
|
assert metafunc.config == py.test.config
|
||||||
assert funcspec.module.__name__ == __name__
|
assert metafunc.module.__name__ == __name__
|
||||||
# XXX actually have the unbound test function here?
|
# XXX actually have an unbound test function here?
|
||||||
assert funcspec.function == TestClass.test_method.im_func
|
assert metafunc.function == TestClass.test_method.im_func
|
||||||
assert funcspec.cls == TestClass
|
assert metafunc.cls == TestClass
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest(p, "-v")
|
result = testdir.runpytest(p, "-v")
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*2 passed in*",
|
"*2 passed in*",
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_arg_twice(self, testdir):
|
|
||||||
testdir.makeconftest("""
|
|
||||||
class ConftestPlugin:
|
|
||||||
def pytest_genfunc(self, funcspec):
|
|
||||||
assert "arg" in funcspec.funcargnames
|
|
||||||
funcspec.addcall(arg=10)
|
|
||||||
funcspec.addcall(arg=20)
|
|
||||||
""")
|
|
||||||
p = testdir.makepyfile("""
|
|
||||||
def test_myfunc(arg):
|
|
||||||
assert arg == 10
|
|
||||||
""")
|
|
||||||
result = testdir.runpytest("-v", p)
|
|
||||||
assert result.stdout.fnmatch_lines([
|
|
||||||
"*test_myfunc*PASS*", # case for 10
|
|
||||||
"*test_myfunc*FAIL*", # case for 20
|
|
||||||
"*1 failed, 1 passed*"
|
|
||||||
])
|
|
||||||
|
|
||||||
def test_two_functions(self, testdir):
|
def test_two_functions(self, testdir):
|
||||||
p = testdir.makepyfile("""
|
p = testdir.makepyfile("""
|
||||||
def pytest_genfunc(funcspec):
|
def pytest_generate_tests(metafunc):
|
||||||
funcspec.addcall(arg1=10)
|
metafunc.addcall(param=10)
|
||||||
funcspec.addcall(arg1=20)
|
metafunc.addcall(param=20)
|
||||||
|
|
||||||
|
def pytest_funcarg__arg1(request):
|
||||||
|
return request.param
|
||||||
|
|
||||||
def test_func1(arg1):
|
def test_func1(arg1):
|
||||||
assert arg1 == 10
|
assert arg1 == 10
|
||||||
|
@ -239,16 +230,21 @@ class TestGenfuncFunctional:
|
||||||
"*1 failed, 3 passed*"
|
"*1 failed, 3 passed*"
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_genfuncarg_inmodule(self, testdir):
|
def test_generate_plugin_and_module(self, testdir):
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest("""
|
||||||
class ConftestPlugin:
|
class ConftestPlugin:
|
||||||
def pytest_genfunc(self, funcspec):
|
def pytest_generate_tests(self, metafunc):
|
||||||
assert "arg1" in funcspec.funcargnames
|
assert "arg1" in metafunc.funcargnames
|
||||||
funcspec.addcall(_id="world", arg1=1, arg2=2)
|
metafunc.addcall(id="world", param=(2,100))
|
||||||
""")
|
""")
|
||||||
p = testdir.makepyfile("""
|
p = testdir.makepyfile("""
|
||||||
def pytest_genfunc(funcspec):
|
def pytest_generate_tests(metafunc):
|
||||||
funcspec.addcall(_id="hello", arg1=10, arg2=10)
|
metafunc.addcall(param=(1,1), id="hello")
|
||||||
|
|
||||||
|
def pytest_funcarg__arg1(request):
|
||||||
|
return request.param[0]
|
||||||
|
def pytest_funcarg__arg2(request):
|
||||||
|
return request.param[1]
|
||||||
|
|
||||||
class TestClass:
|
class TestClass:
|
||||||
def test_myfunc(self, arg1, arg2):
|
def test_myfunc(self, arg1, arg2):
|
||||||
|
|
Loading…
Reference in New Issue