mirror of https://github.com/pytest-dev/pytest.git
select tests by call-id, add and refine documentation around it
--HG-- branch : trunk
This commit is contained in:
parent
3a5d28f3fe
commit
17719b99a1
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
Tracebacks and debugging Python failures
|
debugging Python failures
|
||||||
=================================================================
|
=================================================================
|
||||||
|
|
||||||
Stopping after the first (or N) failures
|
Stopping after the first (or N) failures
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
collect and run doctests from modules and test files.
|
doctest integration for modules and test files.
|
||||||
=========================================================
|
=========================================================
|
||||||
|
|
||||||
By default all files matching the ``test*.txt`` pattern will
|
By default all files matching the ``test*.txt`` pattern will
|
||||||
|
|
|
@ -74,7 +74,6 @@ to see available function arguments (which you can also
|
||||||
think of as "resources").
|
think of as "resources").
|
||||||
|
|
||||||
|
|
||||||
.. _`contact possibilities`: ../contact.html
|
|
||||||
.. _`parametrizing tests, generalized`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/
|
.. _`parametrizing tests, generalized`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/
|
||||||
|
|
||||||
.. _`blog post about the monkeypatch funcarg`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
|
.. _`blog post about the monkeypatch funcarg`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
|
||||||
|
@ -157,15 +156,37 @@ Running this::
|
||||||
E assert 9 < 9
|
E assert 9 < 9
|
||||||
|
|
||||||
test_example.py:7: AssertionError
|
test_example.py:7: AssertionError
|
||||||
==================== 1 failed, 9 passed in 0.04 seconds ====================
|
==================== 1 failed, 9 passed in 0.03 seconds ====================
|
||||||
|
|
||||||
Here is what happens in detail:
|
Note that the ``pytest_generate_tests(metafunc)`` hook is called during
|
||||||
|
the test collection phase. You can have a look at it with this::
|
||||||
|
|
||||||
1. ``pytest_generate_tests(metafunc)`` hook is called once for each test
|
$ py.test --collectonly test_example.py
|
||||||
function. It adds ten new function calls with explicit function arguments.
|
<Directory 'doc-exec-341'>
|
||||||
|
<Module 'test_example.py'>
|
||||||
|
<Function 'test_func[0]'>
|
||||||
|
<Function 'test_func[1]'>
|
||||||
|
<Function 'test_func[2]'>
|
||||||
|
<Function 'test_func[3]'>
|
||||||
|
<Function 'test_func[4]'>
|
||||||
|
<Function 'test_func[5]'>
|
||||||
|
<Function 'test_func[6]'>
|
||||||
|
<Function 'test_func[7]'>
|
||||||
|
<Function 'test_func[8]'>
|
||||||
|
<Function 'test_func[9]'>
|
||||||
|
|
||||||
|
If you want to select only the run with the value ``7`` you could do::
|
||||||
|
|
||||||
|
$ py.test -v -k 7 test_example.py # or -k test_func[7]
|
||||||
|
=========================== test session starts ============================
|
||||||
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0 -- /home/hpk/venv/0/bin/python
|
||||||
|
test path 1: test_example.py
|
||||||
|
|
||||||
|
test_example.py:6: test_func[7] PASSED
|
||||||
|
|
||||||
|
======================== 9 tests deselected by '7' =========================
|
||||||
|
================== 1 passed, 9 deselected in 0.01 seconds ==================
|
||||||
|
|
||||||
2. **execute tests**: ``test_func(numiter)`` is called ten times with
|
|
||||||
ten different arguments.
|
|
||||||
|
|
||||||
.. _`metafunc object`:
|
.. _`metafunc object`:
|
||||||
|
|
||||||
|
|
63
doc/mark.txt
63
doc/mark.txt
|
@ -1,8 +1,8 @@
|
||||||
|
|
||||||
.. _mark:
|
.. _mark:
|
||||||
|
|
||||||
generic mechanism for marking python functions.
|
mark (attribute) python functions
|
||||||
===============================================
|
=================================================================
|
||||||
|
|
||||||
By using the ``py.test.mark`` helper you can instantiate
|
By using the ``py.test.mark`` helper you can instantiate
|
||||||
decorators that will set named meta data on test functions.
|
decorators that will set named meta data on test functions.
|
||||||
|
@ -12,28 +12,29 @@ Marking a single function
|
||||||
|
|
||||||
You can "mark" a test function with meta data like this::
|
You can "mark" a test function with meta data like this::
|
||||||
|
|
||||||
|
import py
|
||||||
@py.test.mark.webtest
|
@py.test.mark.webtest
|
||||||
def test_send_http():
|
def test_send_http():
|
||||||
...
|
...
|
||||||
|
|
||||||
This will set a "Marker" instance as a function attribute named "webtest".
|
This will set the function attribute ``webtest`` to a :py:meth:`MarkInfo`
|
||||||
You can also specify parametrized meta data like this::
|
instance. You can also specify parametrized meta data like this::
|
||||||
|
|
||||||
|
# content of test_mark.py
|
||||||
|
|
||||||
|
import py
|
||||||
@py.test.mark.webtest(firefox=30)
|
@py.test.mark.webtest(firefox=30)
|
||||||
def test_receive():
|
def test_receive():
|
||||||
...
|
pass
|
||||||
|
|
||||||
The named marker can be accessed like this later::
|
@py.test.mark.webtest("functional", firefox=30)
|
||||||
|
def test_run_and_look():
|
||||||
|
pass
|
||||||
|
|
||||||
|
and access it from other places like this::
|
||||||
|
|
||||||
test_receive.webtest.kwargs['firefox'] == 30
|
test_receive.webtest.kwargs['firefox'] == 30
|
||||||
|
test_run_and_look.webtest.args[0] == "functional"
|
||||||
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`:
|
.. _`scoped-marking`:
|
||||||
|
|
||||||
|
@ -43,12 +44,14 @@ Marking whole classes or modules
|
||||||
If you are programming with Python2.6 you may use ``py.test.mark`` decorators
|
If you are programming with Python2.6 you may use ``py.test.mark`` decorators
|
||||||
with classes to apply markers to all its test methods::
|
with classes to apply markers to all its test methods::
|
||||||
|
|
||||||
|
# content of test_mark_classlevel.py
|
||||||
|
import py
|
||||||
@py.test.mark.webtest
|
@py.test.mark.webtest
|
||||||
class TestClass:
|
class TestClass:
|
||||||
def test_startup(self):
|
def test_startup(self):
|
||||||
...
|
pass
|
||||||
def test_startup_and_more(self):
|
def test_startup_and_more(self):
|
||||||
...
|
pass
|
||||||
|
|
||||||
This is equivalent to directly applying the decorator to the
|
This is equivalent to directly applying the decorator to the
|
||||||
two test functions.
|
two test functions.
|
||||||
|
@ -79,8 +82,30 @@ methods defined in the module.
|
||||||
Using "-k MARKNAME" to select tests
|
Using "-k MARKNAME" to select tests
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
|
|
||||||
You can use the ``-k`` command line option to select
|
You can use the ``-k`` command line option to select tests::
|
||||||
tests::
|
|
||||||
|
|
||||||
py.test -k webtest # will only run tests marked as webtest
|
$ py.test -k webtest # will only run tests marked as webtest
|
||||||
|
=========================== test session starts ============================
|
||||||
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
||||||
|
test path 1: /tmp/doc-exec-335
|
||||||
|
|
||||||
|
test_mark.py ..
|
||||||
|
test_mark_classlevel.py ..
|
||||||
|
|
||||||
|
========================= 4 passed in 0.01 seconds =========================
|
||||||
|
|
||||||
|
And you can also run all tests except the ones that match the keyword::
|
||||||
|
|
||||||
|
$ py.test -k-webtest # "-" negates but be careful to have no space before
|
||||||
|
=========================== test session starts ============================
|
||||||
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
||||||
|
test path 1: /tmp/doc-exec-335
|
||||||
|
|
||||||
|
===================== 4 tests deselected by '-webtest' =====================
|
||||||
|
======================= 4 deselected in 0.01 seconds =======================
|
||||||
|
|
||||||
|
API reference for mark related objects
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
.. autoclass:: pytest.plugin.mark.MarkInfo
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
|
|
||||||
helpers for asserting deprecation and other warnings.
|
asserting deprecation and other warnings.
|
||||||
=====================================================
|
=====================================================
|
||||||
|
|
||||||
|
|
||||||
recwarn function argument
|
recwarn function argument
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Sending test report output to XML files or to remote services
|
XML, pastebin services and other reporting
|
||||||
=================================================================
|
=================================================================
|
||||||
|
|
||||||
creating JUnitXML format files
|
creating JUnitXML format files
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
advanced skipping for python test functions, classes or modules.
|
skip and xfail mechanisms
|
||||||
================================================================
|
=====================================================================
|
||||||
|
|
||||||
You can mark test functions for a conditional *skip* or as *xfail*,
|
You can mark test functions for a conditional *skip* or as *xfail*,
|
||||||
expected-to-fail. Skipping a test avoids running a test.
|
expected-to-fail. Skipping a test avoids running a test.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
working with temporary directories and files
|
temporary directories and files
|
||||||
================================================
|
================================================
|
||||||
|
|
||||||
the 'tmpdir' test function argument
|
the 'tmpdir' test function argument
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
automatically discover and run traditional "unittest.py" style tests.
|
unittest.py style testing integration
|
||||||
=====================================================================
|
=====================================================================
|
||||||
|
|
||||||
py.test has limited support for running Python `unittest.py style`_ tests.
|
py.test has limited support for running Python `unittest.py style`_ tests.
|
||||||
|
|
|
@ -1,87 +1,4 @@
|
||||||
"""
|
""" generic mechanism for marking and selecting python functions. """
|
||||||
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 whole classes or modules
|
|
||||||
----------------------------------------------------
|
|
||||||
|
|
||||||
If you are programming with Python2.6 you may use ``py.test.mark`` decorators
|
|
||||||
with classes to apply markers to all its test methods::
|
|
||||||
|
|
||||||
@py.test.mark.webtest
|
|
||||||
class TestClass:
|
|
||||||
def test_startup(self):
|
|
||||||
...
|
|
||||||
def test_startup_and_more(self):
|
|
||||||
...
|
|
||||||
|
|
||||||
This is equivalent to directly applying the decorator to the
|
|
||||||
two test functions.
|
|
||||||
|
|
||||||
To remain compatible with Python2.5 you can also set a
|
|
||||||
``pytestmark`` attribute on a TestClass like this::
|
|
||||||
|
|
||||||
import py
|
|
||||||
|
|
||||||
class TestClass:
|
|
||||||
pytestmark = py.test.mark.webtest
|
|
||||||
|
|
||||||
or if you need to use multiple markers you can use a list::
|
|
||||||
|
|
||||||
import py
|
|
||||||
|
|
||||||
class TestClass:
|
|
||||||
pytestmark = [py.test.mark.webtest, pytest.mark.slowtest]
|
|
||||||
|
|
||||||
You can also set a module level marker::
|
|
||||||
|
|
||||||
import py
|
|
||||||
pytestmark = py.test.mark.webtest
|
|
||||||
|
|
||||||
in which case it will be applied to all functions and
|
|
||||||
methods defined in the module.
|
|
||||||
|
|
||||||
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
|
import py
|
||||||
|
|
||||||
def pytest_namespace():
|
def pytest_namespace():
|
||||||
|
@ -219,7 +136,6 @@ class MarkInfo:
|
||||||
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 pytest_pycollect_makeitem(__multicall__, collector, name, obj):
|
def pytest_pycollect_makeitem(__multicall__, collector, name, obj):
|
||||||
item = __multicall__.execute()
|
item = __multicall__.execute()
|
||||||
if isinstance(item, py.test.collect.Function):
|
if isinstance(item, py.test.collect.Function):
|
||||||
|
@ -237,5 +153,4 @@ def pytest_pycollect_makeitem(__multicall__, collector, name, obj):
|
||||||
if isinstance(mark, MarkDecorator):
|
if isinstance(mark, MarkDecorator):
|
||||||
mark(func)
|
mark(func)
|
||||||
item.keywords.update(py.builtin._getfuncdict(func) or {})
|
item.keywords.update(py.builtin._getfuncdict(func) or {})
|
||||||
|
|
||||||
return item
|
return item
|
||||||
|
|
|
@ -54,7 +54,6 @@ def pytest_collect_file(path, parent):
|
||||||
def pytest_pycollect_makemodule(path, parent):
|
def pytest_pycollect_makemodule(path, parent):
|
||||||
return parent.Module(path, parent)
|
return parent.Module(path, parent)
|
||||||
|
|
||||||
|
|
||||||
def pytest_pycollect_makeitem(__multicall__, collector, name, obj):
|
def pytest_pycollect_makeitem(__multicall__, collector, name, obj):
|
||||||
res = __multicall__.execute()
|
res = __multicall__.execute()
|
||||||
if res is not None:
|
if res is not None:
|
||||||
|
@ -204,7 +203,7 @@ class PyCollectorMixin(PyobjMixin, pytest.collect.Collector):
|
||||||
for callspec in metafunc._calls:
|
for callspec in metafunc._calls:
|
||||||
subname = "%s[%s]" %(name, callspec.id)
|
subname = "%s[%s]" %(name, callspec.id)
|
||||||
function = self.Function(name=subname, parent=self,
|
function = self.Function(name=subname, parent=self,
|
||||||
callspec=callspec, callobj=funcobj)
|
callspec=callspec, callobj=funcobj, keywords={callspec.id:True})
|
||||||
l.append(function)
|
l.append(function)
|
||||||
return l
|
return l
|
||||||
|
|
||||||
|
@ -414,7 +413,7 @@ class Function(FunctionMixin, pytest.collect.Item):
|
||||||
"""
|
"""
|
||||||
_genid = None
|
_genid = None
|
||||||
def __init__(self, name, parent=None, args=None, config=None,
|
def __init__(self, name, parent=None, args=None, config=None,
|
||||||
callspec=None, callobj=_dummy, collection=None):
|
callspec=None, callobj=_dummy, keywords=None, collection=None):
|
||||||
super(Function, self).__init__(name, parent,
|
super(Function, self).__init__(name, parent,
|
||||||
config=config, collection=collection)
|
config=config, collection=collection)
|
||||||
self._args = args
|
self._args = args
|
||||||
|
@ -432,6 +431,8 @@ class Function(FunctionMixin, pytest.collect.Item):
|
||||||
self._obj = callobj
|
self._obj = callobj
|
||||||
self.function = getattr(self.obj, 'im_func', self.obj)
|
self.function = getattr(self.obj, 'im_func', self.obj)
|
||||||
self.keywords.update(py.builtin._getfuncdict(self.obj) or {})
|
self.keywords.update(py.builtin._getfuncdict(self.obj) or {})
|
||||||
|
if keywords:
|
||||||
|
self.keywords.update(keywords)
|
||||||
|
|
||||||
def _getobj(self):
|
def _getobj(self):
|
||||||
name = self.name
|
name = self.name
|
||||||
|
|
|
@ -220,3 +220,19 @@ class TestGeneralUsage:
|
||||||
p1 = testdir.makepyfile("def test_fail(): 0/0")
|
p1 = testdir.makepyfile("def test_fail(): 0/0")
|
||||||
res = testdir.run(py.std.sys.executable, "-m", "py.test", str(p1))
|
res = testdir.run(py.std.sys.executable, "-m", "py.test", str(p1))
|
||||||
assert res.ret == 1
|
assert res.ret == 1
|
||||||
|
|
||||||
|
def test_skip_on_generated_funcarg_id(self, testdir):
|
||||||
|
testdir.makeconftest("""
|
||||||
|
import py
|
||||||
|
def pytest_generate_tests(metafunc):
|
||||||
|
metafunc.addcall({'x': 3}, id='hello-123')
|
||||||
|
def pytest_runtest_setup(item):
|
||||||
|
print (item.keywords)
|
||||||
|
if 'hello-123' in item.keywords:
|
||||||
|
py.test.skip("hello")
|
||||||
|
assert 0
|
||||||
|
""")
|
||||||
|
p = testdir.makepyfile("""def test_func(x): pass""")
|
||||||
|
res = testdir.runpytest(p)
|
||||||
|
assert res.ret == 0
|
||||||
|
res.stdout.fnmatch_lines(["*1 skipped*"])
|
||||||
|
|
Loading…
Reference in New Issue