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)
setattr(func, self.markname, holder)
else:
holder.kwargs.update(self.kwargs)
holder.args += self.args
holder.add(self.args, self.kwargs)
return func
kw = self.kwargs.copy()
kw.update(kwargs)
@ -208,11 +207,23 @@ class MarkInfo:
self.args = args
#: keyword argument dictionary, empty if nothing specified
self.kwargs = kwargs
self._arglist = [(args, kwargs.copy())]
def __repr__(self):
return "<MarkInfo %r args=%r kwargs=%r>" % (
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):
if not isinstance(item, pytest.Function):
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``::
$ py.test -v -m webtest
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1 -- /Users/hpk/venv/1/bin/python
============================= test session starts ==============================
platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3 -- /Users/hpk/venv/1/bin/python
collecting ... collected 2 items
test_server.py:3: test_send_http PASSED
=================== 1 tests deselected by "-m 'webtest'" ===================
================== 1 passed, 1 deselected in 0.01 seconds ==================
===================== 1 tests deselected by "-m 'webtest'" =====================
==================== 1 passed, 1 deselected in 0.03 seconds ====================
Or the inverse, running all tests except the webtest ones::
$ py.test -v -m "not webtest"
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1 -- /Users/hpk/venv/1/bin/python
============================= test session starts ==============================
platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3 -- /Users/hpk/venv/1/bin/python
collecting ... collected 2 items
test_server.py:6: test_something_quick PASSED
================= 1 tests deselected by "-m 'not webtest'" =================
================== 1 passed, 1 deselected in 0.01 seconds ==================
=================== 1 tests deselected by "-m 'not webtest'" ===================
==================== 1 passed, 1 deselected in 0.03 seconds ====================
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
methods defined in the module.
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::
$ py.test -k send_http # running with the above defined examples
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
============================= test session starts ==============================
platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3
collecting ... collected 4 items
test_server.py .
=================== 3 tests deselected by '-ksend_http' ====================
================== 1 passed, 3 deselected in 0.02 seconds ==================
===================== 3 tests deselected by '-ksend_http' ======================
==================== 1 passed, 3 deselected in 0.06 seconds ====================
And you can also run all tests except the ones that match the keyword::
$ py.test -k-send_http
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
============================= test session starts ==============================
platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3
collecting ... collected 4 items
test_mark_classlevel.py ..
test_server.py .
=================== 1 tests deselected by '-k-send_http' ===================
================== 3 passed, 1 deselected in 0.03 seconds ==================
===================== 1 tests deselected by '-k-send_http' =====================
==================== 3 passed, 1 deselected in 0.05 seconds ====================
Or to only select the class::
$ py.test -kTestClass
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
============================= test session starts ==============================
platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3
collecting ... collected 4 items
test_mark_classlevel.py ..
=================== 2 tests deselected by '-kTestClass' ====================
================== 2 passed, 2 deselected in 0.02 seconds ==================
===================== 2 tests deselected by '-kTestClass' ======================
==================== 2 passed, 2 deselected in 0.04 seconds ====================
.. _`adding a custom marker from a plugin`:
@ -221,24 +222,24 @@ and an example invocations specifying a different environment than what
the test needs::
$ py.test -E stage2
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
============================= test session starts ==============================
platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3
collecting ... collected 1 items
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::
$ py.test -E stage1
=========================== test session starts ============================
platform darwin -- Python 2.7.1 -- pytest-2.2.1
============================= test session starts ==============================
platform darwin -- Python 2.7.1 -- pytest-2.2.2.dev3
collecting ... collected 1 items
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::
@ -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.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.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):
pytest.raises(TypeError, '''
testdir.getitem("""
@ -259,6 +267,23 @@ class TestFunctional:
"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:
def test_select_simple(self, testdir):