internally keep multiple applications of the same markers as separate

entities such that the new iter() API can iterate over pytest.mark
function attributes, getting all such applications.  See added example
for more info.
This commit is contained in:
holger krekel 2011-12-28 15:47:18 +00:00
parent 85f2a78005
commit ccc1b21ebd
3 changed files with 104 additions and 28 deletions

View File

@ -191,8 +191,7 @@ class MarkDecorator:
holder = MarkInfo(self.markname, self.args, self.kwargs) holder = MarkInfo(self.markname, self.args, self.kwargs)
setattr(func, self.markname, holder) setattr(func, self.markname, holder)
else: else:
holder.kwargs.update(self.kwargs) holder.add(self.args, self.kwargs)
holder.args += self.args
return func return func
kw = self.kwargs.copy() kw = self.kwargs.copy()
kw.update(kwargs) kw.update(kwargs)
@ -208,11 +207,23 @@ class MarkInfo:
self.args = args self.args = args
#: keyword argument dictionary, empty if nothing specified #: keyword argument dictionary, empty if nothing specified
self.kwargs = kwargs self.kwargs = kwargs
self._arglist = [(args, kwargs.copy())]
def __repr__(self): def __repr__(self):
return "<MarkInfo %r args=%r kwargs=%r>" % ( return "<MarkInfo %r args=%r kwargs=%r>" % (
self.name, self.args, self.kwargs) self.name, self.args, self.kwargs)
def add(self, args, kwargs):
""" add a MarkInfo with the given args and kwargs. """
self._arglist.append((args, kwargs))
self.args += args
self.kwargs.update(kwargs)
def __iter__(self):
""" yield MarkInfo objects each relating to a marking-call. """
for args, kwargs in self._arglist:
yield MarkInfo(self.name, args, kwargs)
def pytest_itemcollected(item): def pytest_itemcollected(item):
if not isinstance(item, pytest.Function): if not isinstance(item, pytest.Function):
return return

View File

@ -25,26 +25,26 @@ You can "mark" a test function with custom metadata like this::
You can then restrict a test run to only run tests marked with ``webtest``:: You can then restrict a test run to only run tests marked with ``webtest``::
$ py.test -v -m webtest $ py.test -v -m webtest
=========================== test session starts ============================ ============================= test session starts ==============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1 -- /Users/hpk/venv/1/bin/python platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3 -- /Users/hpk/venv/1/bin/python
collecting ... collected 2 items collecting ... collected 2 items
test_server.py:3: test_send_http PASSED test_server.py:3: test_send_http PASSED
=================== 1 tests deselected by "-m 'webtest'" =================== ===================== 1 tests deselected by "-m 'webtest'" =====================
================== 1 passed, 1 deselected in 0.01 seconds ================== ==================== 1 passed, 1 deselected in 0.03 seconds ====================
Or the inverse, running all tests except the webtest ones:: Or the inverse, running all tests except the webtest ones::
$ py.test -v -m "not webtest" $ py.test -v -m "not webtest"
=========================== test session starts ============================ ============================= test session starts ==============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1 -- /Users/hpk/venv/1/bin/python platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3 -- /Users/hpk/venv/1/bin/python
collecting ... collected 2 items collecting ... collected 2 items
test_server.py:6: test_something_quick PASSED test_server.py:6: test_something_quick PASSED
================= 1 tests deselected by "-m 'not webtest'" ================= =================== 1 tests deselected by "-m 'not webtest'" ===================
================== 1 passed, 1 deselected in 0.01 seconds ================== ==================== 1 passed, 1 deselected in 0.03 seconds ====================
Registering markers Registering markers
------------------------------------- -------------------------------------
@ -134,6 +134,7 @@ You can also set a module level marker::
in which case it will be applied to all functions and in which case it will be applied to all functions and
methods defined in the module. methods defined in the module.
Using ``-k TEXT`` to select tests Using ``-k TEXT`` to select tests
---------------------------------------------------- ----------------------------------------------------
@ -141,39 +142,39 @@ You can use the ``-k`` command line option to only run tests with names matching
the given argument:: the given argument::
$ py.test -k send_http # running with the above defined examples $ py.test -k send_http # running with the above defined examples
=========================== test session starts ============================ ============================= test session starts ==============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1 platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3
collecting ... collected 4 items collecting ... collected 4 items
test_server.py . test_server.py .
=================== 3 tests deselected by '-ksend_http' ==================== ===================== 3 tests deselected by '-ksend_http' ======================
================== 1 passed, 3 deselected in 0.02 seconds ================== ==================== 1 passed, 3 deselected in 0.06 seconds ====================
And you can also run all tests except the ones that match the keyword:: And you can also run all tests except the ones that match the keyword::
$ py.test -k-send_http $ py.test -k-send_http
=========================== test session starts ============================ ============================= test session starts ==============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1 platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3
collecting ... collected 4 items collecting ... collected 4 items
test_mark_classlevel.py .. test_mark_classlevel.py ..
test_server.py . test_server.py .
=================== 1 tests deselected by '-k-send_http' =================== ===================== 1 tests deselected by '-k-send_http' =====================
================== 3 passed, 1 deselected in 0.03 seconds ================== ==================== 3 passed, 1 deselected in 0.05 seconds ====================
Or to only select the class:: Or to only select the class::
$ py.test -kTestClass $ py.test -kTestClass
=========================== test session starts ============================ ============================= test session starts ==============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1 platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3
collecting ... collected 4 items collecting ... collected 4 items
test_mark_classlevel.py .. test_mark_classlevel.py ..
=================== 2 tests deselected by '-kTestClass' ==================== ===================== 2 tests deselected by '-kTestClass' ======================
================== 2 passed, 2 deselected in 0.02 seconds ================== ==================== 2 passed, 2 deselected in 0.04 seconds ====================
.. _`adding a custom marker from a plugin`: .. _`adding a custom marker from a plugin`:
@ -221,24 +222,24 @@ and an example invocations specifying a different environment than what
the test needs:: the test needs::
$ py.test -E stage2 $ py.test -E stage2
=========================== test session starts ============================ ============================= test session starts ==============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1 platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3
collecting ... collected 1 items collecting ... collected 1 items
test_someenv.py s test_someenv.py s
======================== 1 skipped in 0.02 seconds ========================= ========================== 1 skipped in 0.03 seconds ===========================
and here is one that specifies exactly the environment needed:: and here is one that specifies exactly the environment needed::
$ py.test -E stage1 $ py.test -E stage1
=========================== test session starts ============================ ============================= test session starts ==============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1 platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3
collecting ... collected 1 items collecting ... collected 1 items
test_someenv.py . test_someenv.py .
========================= 1 passed in 0.01 seconds ========================= =========================== 1 passed in 0.03 seconds ===========================
The ``--markers`` option always gives you a list of available markers:: The ``--markers`` option always gives you a list of available markers::
@ -254,4 +255,43 @@ The ``--markers`` option always gives you a list of available markers::
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible. @pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
@pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible. @pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
Reading markers which were set from multiple places
----------------------------------------------------
.. versionadded: 2.2.2
If you are heavily using markers in your test suite you may encounter the case where a marker is applied several times to a test function. From plugin
code you can read over all such settings. Example::
# content of test_mark_three_times.py
import pytest
pytestmark = pytest.mark.glob("module", x=1)
@pytest.mark.glob("class", x=2)
class TestClass:
@pytest.mark.glob("function", x=3)
def test_something(self):
pass
Here we have the marker "glob" applied three times to the same
test function. From a conftest file we can read it like this::
# content of conftest.py
def pytest_runtest_setup(item):
g = getattr(item.obj, 'glob', None)
if g is not None:
for info in g:
print ("glob args=%s kwargs=%s" %(info.args, info.kwargs))
Let's run this without capturing output and see what we get::
$ py.test -q -s
collecting ... collected 2 items
..
2 passed in 0.04 seconds
glob args=('function',) kwargs={'x': 3}
glob args=('module',) kwargs={'x': 1}
glob args=('class',) kwargs={'x': 2}

View File

@ -230,6 +230,14 @@ class TestFunctional:
assert marker.args == ("pos0", "pos1") assert marker.args == ("pos0", "pos1")
assert marker.kwargs == {'x': 3, 'y': 2, 'z': 4} assert marker.kwargs == {'x': 3, 'y': 2, 'z': 4}
# test the new __iter__ interface
l = list(marker)
assert len(l) == 3
assert l[0].args == ("pos0",)
pytest.xfail(reason="needs reordering of parametrize transfermarks")
assert l[1].args == ()
assert l[2].args == ("pos1", )
def test_mark_other(self, testdir): def test_mark_other(self, testdir):
pytest.raises(TypeError, ''' pytest.raises(TypeError, '''
testdir.getitem(""" testdir.getitem("""
@ -259,6 +267,23 @@ class TestFunctional:
"keyword: *hello*" "keyword: *hello*"
]) ])
def test_merging_markers_two_functions(self, testdir):
p = testdir.makepyfile("""
import pytest
@pytest.mark.hello("pos1", z=4)
@pytest.mark.hello("pos0", z=3)
def test_func(self):
pass
""")
items, rec = testdir.inline_genitems(p)
item, = items
keywords = item.keywords
marker = keywords['hello']
l = list(marker)
assert len(l) == 2
assert l[0].args == ("pos0",)
assert l[1].args == ("pos1",)
class TestKeywordSelection: class TestKeywordSelection:
def test_select_simple(self, testdir): def test_select_simple(self, testdir):