test_ok2/py/plugin/pytest_mark.py

149 lines
4.3 KiB
Python

"""
generic mechanism for marking python functions.
By using the ``py.test.mark`` helper you can instantiate
decorators that will set named meta data on test functions.
Marking a single function
----------------------------------------------------
You can "mark" a test function with meta data like this::
@py.test.mark.webtest
def test_send_http():
...
This will set a "Marker" instance as a function attribute named "webtest".
You can also specify parametrized meta data like this::
@py.test.mark.webtest(firefox=30)
def test_receive():
...
The named marker can be accessed like this later::
test_receive.webtest.kwargs['firefox'] == 30
In addition to set key-value pairs you can also use positional arguments::
@py.test.mark.webtest("triangular")
def test_receive():
...
and later access it with ``test_receive.webtest.args[0] == 'triangular``.
.. _`scoped-marking`:
Marking classes or modules
----------------------------------------------------
To mark all methods of a class set a ``pytestmark`` attribute like this::
import py
class TestClass:
pytestmark = py.test.mark.webtest
You can re-use the same markers that you would use for decorating
a function - in fact this marker decorator will be applied
to all test methods of the class.
You can also set a module level marker::
import py
pytestmark = py.test.mark.webtest
in which case then the marker decorator will be applied to all functions and
methods defined in the module.
The order in which marker functions are called is this::
per-function (upon import of module already)
per-class
per-module
Later called markers may overwrite previous key-value settings.
Positional arguments are all appended to the same 'args' list
of the Marker object.
Using "-k MARKNAME" to select tests
----------------------------------------------------
You can use the ``-k`` command line option to select
tests::
py.test -k webtest # will only run tests marked as webtest
"""
import py
def pytest_namespace():
return {'mark': Mark()}
class Mark(object):
def __getattr__(self, name):
if name[0] == "_":
raise AttributeError(name)
return MarkerDecorator(name)
class MarkerDecorator:
""" decorator for setting function attributes. """
def __init__(self, name):
self.markname = name
self.kwargs = {}
self.args = []
def __repr__(self):
d = self.__dict__.copy()
name = d.pop('markname')
return "<MarkerDecorator %r %r>" %(name, d)
def __call__(self, *args, **kwargs):
if args:
if len(args) == 1 and hasattr(args[0], '__call__'):
func = args[0]
holder = getattr(func, self.markname, None)
if holder is None:
holder = Marker(self.markname, self.args, self.kwargs)
setattr(func, self.markname, holder)
else:
holder.kwargs.update(self.kwargs)
holder.args.extend(self.args)
return func
else:
self.args.extend(args)
self.kwargs.update(kwargs)
return self
class Marker:
def __init__(self, name, args, kwargs):
self._name = name
self.args = args
self.kwargs = kwargs
def __getattr__(self, name):
if name[0] != '_' and name in self.kwargs:
py.log._apiwarn("1.1", "use .kwargs attribute to access key-values")
return self.kwargs[name]
raise AttributeError(name)
def __repr__(self):
return "<Marker %r args=%r kwargs=%r>" % (
self._name, self.args, self.kwargs)
def pytest_pycollect_makeitem(__multicall__, collector, name, obj):
item = __multicall__.execute()
if isinstance(item, py.test.collect.Function):
cls = collector.getparent(py.test.collect.Class)
mod = collector.getparent(py.test.collect.Module)
func = item.obj
func = getattr(func, '__func__', func) # py3
func = getattr(func, 'im_func', func) # py2
for parent in [x for x in (mod, cls) if x]:
marker = getattr(parent.obj, 'pytestmark', None)
if isinstance(marker, MarkerDecorator):
marker(func)
return item