extend and refine test marking
- allow to mark tests via a "pytestmark" name at class/module level. - make combined positional args of marker calls available via an _args argument --HG-- branch : trunk
This commit is contained in:
parent
9ac4faf3af
commit
4a76c096da
|
@ -1,7 +1,7 @@
|
||||||
"""
|
"""
|
||||||
mark test functions with keywords that may hold values.
|
mark test functions with keywords that may hold values.
|
||||||
|
|
||||||
Marking functions and setting rich attributes
|
Marking functions by a decorator
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
|
|
||||||
By default, all filename parts and class/function names of a test
|
By default, all filename parts and class/function names of a test
|
||||||
|
@ -30,8 +30,29 @@ In addition to keyword arguments you can also use positional arguments::
|
||||||
def test_receive():
|
def test_receive():
|
||||||
...
|
...
|
||||||
|
|
||||||
after which ``test_receive.webtest._1 == 'triangular`` hold true.
|
after which ``test_receive.webtest._args[0] == 'triangular`` holds true.
|
||||||
|
|
||||||
|
|
||||||
|
Marking classes or modules
|
||||||
|
----------------------------------------------------
|
||||||
|
|
||||||
|
To mark all methods of a class you can set a class-level attribute::
|
||||||
|
|
||||||
|
class TestClass:
|
||||||
|
pytestmark = py.test.mark.webtest
|
||||||
|
|
||||||
|
the marker function will be applied to all test methods.
|
||||||
|
|
||||||
|
If you set a marker it inside a test module like this::
|
||||||
|
|
||||||
|
pytestmark = py.test.mark.webtest
|
||||||
|
|
||||||
|
the marker will be applied to all functions and methods of
|
||||||
|
that module. The module marker is applied last.
|
||||||
|
|
||||||
|
Outer ``pytestmark`` keywords will overwrite inner keyword
|
||||||
|
values. Positional arguments are all appeneded to the
|
||||||
|
same '_args' list.
|
||||||
"""
|
"""
|
||||||
import py
|
import py
|
||||||
|
|
||||||
|
@ -49,6 +70,8 @@ class MarkerDecorator:
|
||||||
""" decorator for setting function attributes. """
|
""" decorator for setting function attributes. """
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.markname = name
|
self.markname = name
|
||||||
|
self.kwargs = {}
|
||||||
|
self.args = []
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
d = self.__dict__.copy()
|
d = self.__dict__.copy()
|
||||||
|
@ -57,19 +80,41 @@ class MarkerDecorator:
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
if args:
|
if args:
|
||||||
if hasattr(args[0], '__call__'):
|
if len(args) == 1 and hasattr(args[0], '__call__'):
|
||||||
func = args[0]
|
func = args[0]
|
||||||
mh = MarkHolder(getattr(self, 'kwargs', {}))
|
holder = getattr(func, self.markname, None)
|
||||||
setattr(func, self.markname, mh)
|
if holder is None:
|
||||||
|
holder = MarkHolder(self.markname, self.args, self.kwargs)
|
||||||
|
setattr(func, self.markname, holder)
|
||||||
|
else:
|
||||||
|
holder.__dict__.update(self.kwargs)
|
||||||
|
holder._args.extend(self.args)
|
||||||
return func
|
return func
|
||||||
# not a function so we memorize all args/kwargs settings
|
else:
|
||||||
for i, arg in enumerate(args):
|
self.args.extend(args)
|
||||||
kwargs["_" + str(i)] = arg
|
self.kwargs.update(kwargs)
|
||||||
if hasattr(self, 'kwargs'):
|
|
||||||
raise TypeError("double mark-keywords?")
|
|
||||||
self.kwargs = kwargs.copy()
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
class MarkHolder:
|
class MarkHolder:
|
||||||
def __init__(self, kwargs):
|
def __init__(self, name, args, kwargs):
|
||||||
|
self._name = name
|
||||||
|
self._args = args
|
||||||
|
self._kwargs = kwargs
|
||||||
self.__dict__.update(kwargs)
|
self.__dict__.update(kwargs)
|
||||||
|
|
||||||
|
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 = getattr(item.obj, 'im_func', item.obj)
|
||||||
|
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
|
||||||
|
|
|
@ -1,25 +1,47 @@
|
||||||
import py
|
import py
|
||||||
from _py.test.plugin.pytest_keyword import Mark
|
from _py.test.plugin.pytest_keyword import Mark
|
||||||
|
|
||||||
def test_pytest_mark_api():
|
class TestMark:
|
||||||
|
def test_pytest_mark_notcallable(self):
|
||||||
mark = Mark()
|
mark = Mark()
|
||||||
py.test.raises(TypeError, "mark(x=3)")
|
py.test.raises(TypeError, "mark()")
|
||||||
|
|
||||||
|
def test_pytest_mark_bare(self):
|
||||||
|
mark = Mark()
|
||||||
def f(): pass
|
def f(): pass
|
||||||
mark.hello(f)
|
mark.hello(f)
|
||||||
assert f.hello
|
assert f.hello
|
||||||
|
|
||||||
|
def test_pytest_mark_keywords(self):
|
||||||
|
mark = Mark()
|
||||||
|
def f(): pass
|
||||||
mark.world(x=3, y=4)(f)
|
mark.world(x=3, y=4)(f)
|
||||||
assert f.world
|
assert f.world
|
||||||
assert f.world.x == 3
|
assert f.world.x == 3
|
||||||
assert f.world.y == 4
|
assert f.world.y == 4
|
||||||
|
|
||||||
|
def test_apply_multiple_and_merge(self):
|
||||||
|
mark = Mark()
|
||||||
|
def f(): pass
|
||||||
|
marker = mark.world
|
||||||
|
mark.world(x=3)(f)
|
||||||
|
assert f.world.x == 3
|
||||||
|
mark.world(y=4)(f)
|
||||||
|
assert f.world.x == 3
|
||||||
|
assert f.world.y == 4
|
||||||
|
mark.world(y=1)(f)
|
||||||
|
assert f.world.y == 1
|
||||||
|
assert len(f.world._args) == 0
|
||||||
|
|
||||||
|
def test_pytest_mark_positional(self):
|
||||||
|
mark = Mark()
|
||||||
|
def f(): pass
|
||||||
mark.world("hello")(f)
|
mark.world("hello")(f)
|
||||||
assert f.world._0 == "hello"
|
assert f.world._args[0] == "hello"
|
||||||
|
mark.world("world")(f)
|
||||||
|
|
||||||
py.test.raises(TypeError, "mark.some(x=3)(f=5)")
|
class TestFunctional:
|
||||||
|
def test_mark_per_function(self, testdir):
|
||||||
def test_mark_plugin(testdir):
|
|
||||||
p = testdir.makepyfile("""
|
p = testdir.makepyfile("""
|
||||||
import py
|
import py
|
||||||
@py.test.mark.hello
|
@py.test.mark.hello
|
||||||
|
@ -28,3 +50,56 @@ def test_mark_plugin(testdir):
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest(p)
|
result = testdir.runpytest(p)
|
||||||
assert result.stdout.fnmatch_lines(["*passed*"])
|
assert result.stdout.fnmatch_lines(["*passed*"])
|
||||||
|
|
||||||
|
def test_mark_per_module(self, testdir):
|
||||||
|
item = testdir.getitem("""
|
||||||
|
import py
|
||||||
|
pytestmark = py.test.mark.hello
|
||||||
|
def test_func():
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
keywords = item.readkeywords()
|
||||||
|
assert 'hello' in keywords
|
||||||
|
|
||||||
|
def test_mark_per_class(self, testdir):
|
||||||
|
modcol = testdir.getmodulecol("""
|
||||||
|
import py
|
||||||
|
class TestClass:
|
||||||
|
pytestmark = py.test.mark.hello
|
||||||
|
def test_func(self):
|
||||||
|
assert TestClass.test_func.hello
|
||||||
|
""")
|
||||||
|
clscol = modcol.collect()[0]
|
||||||
|
item = clscol.collect()[0].collect()[0]
|
||||||
|
keywords = item.readkeywords()
|
||||||
|
assert 'hello' in keywords
|
||||||
|
|
||||||
|
def test_merging_markers(self, testdir):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
import py
|
||||||
|
pytestmark = py.test.mark.hello("pos1", x=1, y=2)
|
||||||
|
class TestClass:
|
||||||
|
# classlevel overrides module level
|
||||||
|
pytestmark = py.test.mark.hello(x=3)
|
||||||
|
@py.test.mark.hello("pos0", z=4)
|
||||||
|
def test_func(self):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
items, rec = testdir.inline_genitems(p)
|
||||||
|
item, = items
|
||||||
|
keywords = item.readkeywords()
|
||||||
|
marker = keywords['hello']
|
||||||
|
assert marker._args == ["pos0", "pos1"]
|
||||||
|
assert marker.x == 3
|
||||||
|
assert marker.y == 2
|
||||||
|
assert marker.z == 4
|
||||||
|
|
||||||
|
def test_mark_other(self, testdir):
|
||||||
|
item = testdir.getitem("""
|
||||||
|
import py
|
||||||
|
class pytestmark:
|
||||||
|
pass
|
||||||
|
def test_func():
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
keywords = item.readkeywords()
|
||||||
|
|
Loading…
Reference in New Issue