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:
parent
85f2a78005
commit
ccc1b21ebd
|
@ -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
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue