introduce a new -m mark_expression option
This commit is contained in:
parent
bc8ee95e72
commit
36c42b5c15
|
@ -7,9 +7,10 @@ Changes between 2.1.3 and XXX 2.2.0
|
|||
allowing to avoid typos and maintain a well described set of markers
|
||||
for your test suite. See exaples at http://pytest.org/latest/mark.html
|
||||
and its links.
|
||||
- XXX introduce "-m marker" option to select tests based on markers
|
||||
(this is a stricter more predictable version of '-k' which also matches
|
||||
substrings and compares against the test function name etc.)
|
||||
- introduce "-m marker" option to select tests based on markers
|
||||
(this is a stricter and more predictable version of '-k' in that
|
||||
"-m" only matches complete markers and has more obvious rules
|
||||
for and/or semantics.
|
||||
- new feature to help optimizing the speed of your tests:
|
||||
--durations=N option for displaying N slowest test calls
|
||||
and setup/teardown methods.
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#
|
||||
__version__ = '2.2.0.dev6'
|
||||
__version__ = '2.2.0.dev7'
|
||||
|
|
|
@ -14,6 +14,12 @@ def pytest_addoption(parser):
|
|||
"Terminate expression with ':' to make the first match match "
|
||||
"all subsequent tests (usually file-order). ")
|
||||
|
||||
group._addoption("-m",
|
||||
action="store", dest="markexpr", default="", metavar="MARKEXPR",
|
||||
help="only run tests which match given mark expression. "
|
||||
"An expression is a python expression which can use "
|
||||
"marker names.")
|
||||
|
||||
group.addoption("--markers", action="store_true", help=
|
||||
"show markers (builtin, plugin and per-project ones).")
|
||||
|
||||
|
@ -34,10 +40,11 @@ pytest_cmdline_main.tryfirst = True
|
|||
|
||||
def pytest_collection_modifyitems(items, config):
|
||||
keywordexpr = config.option.keyword
|
||||
if not keywordexpr:
|
||||
matchexpr = config.option.markexpr
|
||||
if not keywordexpr and not matchexpr:
|
||||
return
|
||||
selectuntil = False
|
||||
if keywordexpr[-1] == ":":
|
||||
if keywordexpr[-1:] == ":":
|
||||
selectuntil = True
|
||||
keywordexpr = keywordexpr[:-1]
|
||||
|
||||
|
@ -47,14 +54,27 @@ def pytest_collection_modifyitems(items, config):
|
|||
if keywordexpr and skipbykeyword(colitem, keywordexpr):
|
||||
deselected.append(colitem)
|
||||
else:
|
||||
remaining.append(colitem)
|
||||
if selectuntil:
|
||||
keywordexpr = None
|
||||
if matchexpr:
|
||||
if not matchmark(colitem, matchexpr):
|
||||
deselected.append(colitem)
|
||||
continue
|
||||
remaining.append(colitem)
|
||||
|
||||
if deselected:
|
||||
config.hook.pytest_deselected(items=deselected)
|
||||
items[:] = remaining
|
||||
|
||||
class BoolDict:
|
||||
def __init__(self, mydict):
|
||||
self._mydict = mydict
|
||||
def __getitem__(self, name):
|
||||
return name in self._mydict
|
||||
|
||||
def matchmark(colitem, matchexpr):
|
||||
return eval(matchexpr, {}, BoolDict(colitem.obj.__dict__))
|
||||
|
||||
def pytest_configure(config):
|
||||
if config.option.strict:
|
||||
pytest.mark._config = config
|
||||
|
|
|
@ -440,8 +440,15 @@ class TerminalReporter:
|
|||
|
||||
def summary_deselected(self):
|
||||
if 'deselected' in self.stats:
|
||||
l = []
|
||||
k = self.config.option.keyword
|
||||
if k:
|
||||
l.append("-k%s" % k)
|
||||
m = self.config.option.markexpr
|
||||
if m:
|
||||
l.append("-m %r" % m)
|
||||
self.write_sep("=", "%d tests deselected by %r" %(
|
||||
len(self.stats['deselected']), self.config.option.keyword), bold=True)
|
||||
len(self.stats['deselected']), " ".join(l)), bold=True)
|
||||
|
||||
def repr_pythonversion(v=None):
|
||||
if v is None:
|
||||
|
|
|
@ -40,7 +40,7 @@ clean:
|
|||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
install: html
|
||||
@rsync -avz _build/html/ pytest.org:/www/pytest.org/latest
|
||||
@rsync -avz _build/html/ pytest.org:/www/pytest.org/2.2.0.dev7
|
||||
|
||||
installpdf: latexpdf
|
||||
@scp $(BUILDDIR)/latex/pytest.pdf pytest.org:/www/pytest.org/latest
|
||||
|
|
|
@ -26,8 +26,8 @@ Welcome to pytest!
|
|||
- **supports functional testing and complex test setups**
|
||||
|
||||
- (new in 2.2) :ref:`durations`
|
||||
- (much improved in 2.2) :ref:`marking and test selection <mark>`
|
||||
- advanced :ref:`skip and xfail`
|
||||
- generic :ref:`marking and test selection <mark>`
|
||||
- can :ref:`distribute tests to multiple CPUs <xdistcpu>` through :ref:`xdist plugin <xdist>`
|
||||
- can :ref:`continuously re-run failing tests <looponfailing>`
|
||||
- many :ref:`builtin helpers <pytest helpers>`
|
||||
|
|
71
doc/mark.txt
71
doc/mark.txt
|
@ -28,16 +28,34 @@ You can "mark" a test function with custom metadata like this::
|
|||
@pytest.mark.webtest
|
||||
def test_send_http():
|
||||
pass # perform some webtest test for your app
|
||||
def test_something_quick():
|
||||
pass
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
You can restrict a test run only tests marked with ``webtest`` like this::
|
||||
You can then restrict a test run to only run tests marked with ``webtest``::
|
||||
|
||||
$ py.test -m webtest
|
||||
$ py.test -v -m webtest
|
||||
============================= test session starts ==============================
|
||||
platform darwin -- Python 2.7.1 -- pytest-2.2.0.dev6 -- /Users/hpk/venv/0/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 ====================
|
||||
|
||||
Or the inverse, running all tests except the webtest ones::
|
||||
|
||||
$ py.test -m "not webtest"
|
||||
$ py.test -v -m "not webtest"
|
||||
============================= test session starts ==============================
|
||||
platform darwin -- Python 2.7.1 -- pytest-2.2.0.dev6 -- /Users/hpk/venv/0/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 ====================
|
||||
|
||||
Registering markers
|
||||
-------------------------------------
|
||||
|
@ -53,9 +71,19 @@ Registering markers for your test suite is simple::
|
|||
markers =
|
||||
webtest: mark a test as a webtest.
|
||||
|
||||
You can ask which markers exist for your test suite::
|
||||
You can ask which markers exist for your test suite - the list includes our just defined ``webtest`` markers::
|
||||
|
||||
$ py.test --markers
|
||||
@pytest.mark.webtest: mark a test as a webtest.
|
||||
|
||||
@pytest.mark.skipif(*conditions): skip the given test function if evaluation of all conditions has a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform.
|
||||
|
||||
@pytest.mark.xfail(*conditions, reason=None, run=True): mark the the test function as an expected failure. Optionally specify a reason and run=False if you don't even want to execute the test function. Any positional condition strings will be evaluated (like with skipif) and if one is False the marker will not be applied.
|
||||
|
||||
@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.
|
||||
|
||||
|
||||
For an example on how to add and work markers from a plugin, see
|
||||
:ref:`adding a custom marker from a plugin`.
|
||||
|
@ -118,39 +146,42 @@ methods defined in the module.
|
|||
Using ``-k TEXT`` to select tests
|
||||
----------------------------------------------------
|
||||
|
||||
You can use the ``-k`` command line option to select tests::
|
||||
You can use the ``-k`` command line option to only run tests with names that match the given argument::
|
||||
|
||||
$ py.test -k webtest # running with the above defined examples yields
|
||||
=========================== test session starts ============================
|
||||
platform darwin -- Python 2.7.1 -- pytest-2.1.3
|
||||
$ py.test -k send_http # running with the above defined examples
|
||||
============================= test session starts ==============================
|
||||
platform darwin -- Python 2.7.1 -- pytest-2.2.0.dev6
|
||||
collecting ... collected 4 items
|
||||
|
||||
test_mark.py ..
|
||||
test_mark_classlevel.py ..
|
||||
test_server.py .
|
||||
|
||||
========================= 4 passed in 0.03 seconds =========================
|
||||
===================== 3 tests deselected by '-ksend_http' ======================
|
||||
==================== 1 passed, 3 deselected in 0.02 seconds ====================
|
||||
|
||||
And you can also run all tests except the ones that match the keyword::
|
||||
|
||||
$ py.test -k-webtest
|
||||
=========================== test session starts ============================
|
||||
platform darwin -- Python 2.7.1 -- pytest-2.1.3
|
||||
$ py.test -k-send_http
|
||||
============================= test session starts ==============================
|
||||
platform darwin -- Python 2.7.1 -- pytest-2.2.0.dev6
|
||||
collecting ... collected 4 items
|
||||
|
||||
===================== 4 tests deselected by '-webtest' =====================
|
||||
======================= 4 deselected in 0.02 seconds =======================
|
||||
test_mark_classlevel.py ..
|
||||
test_server.py .
|
||||
|
||||
===================== 1 tests deselected by '-k-send_http' =====================
|
||||
==================== 3 passed, 1 deselected in 0.03 seconds ====================
|
||||
|
||||
Or to only select the class::
|
||||
|
||||
$ py.test -kTestClass
|
||||
=========================== test session starts ============================
|
||||
platform darwin -- Python 2.7.1 -- pytest-2.1.3
|
||||
============================= test session starts ==============================
|
||||
platform darwin -- Python 2.7.1 -- pytest-2.2.0.dev6
|
||||
collecting ... collected 4 items
|
||||
|
||||
test_mark_classlevel.py ..
|
||||
|
||||
==================== 2 tests deselected by 'TestClass' =====================
|
||||
================== 2 passed, 2 deselected in 0.02 seconds ==================
|
||||
===================== 2 tests deselected by '-kTestClass' ======================
|
||||
==================== 2 passed, 2 deselected in 0.02 seconds ====================
|
||||
|
||||
API reference for mark related objects
|
||||
------------------------------------------------
|
||||
|
|
2
setup.py
2
setup.py
|
@ -24,7 +24,7 @@ def main():
|
|||
name='pytest',
|
||||
description='py.test: simple powerful testing with Python',
|
||||
long_description = long_description,
|
||||
version='2.2.0.dev6',
|
||||
version='2.2.0.dev7',
|
||||
url='http://pytest.org',
|
||||
license='MIT license',
|
||||
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
||||
|
|
|
@ -114,6 +114,30 @@ def test_strict_prohibits_unregistered_markers(testdir):
|
|||
"*unregisteredmark*not*registered*",
|
||||
])
|
||||
|
||||
@pytest.mark.multi(spec=[
|
||||
("xyz", ("test_one",)),
|
||||
("xyz and xyz2", ()),
|
||||
("xyz2", ("test_two",)),
|
||||
("xyz or xyz2", ("test_one", "test_two"),)
|
||||
])
|
||||
def test_mark_option(spec, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
@pytest.mark.xyz
|
||||
def test_one():
|
||||
pass
|
||||
@pytest.mark.xyz2
|
||||
def test_two():
|
||||
pass
|
||||
""")
|
||||
opt, passed_result = spec
|
||||
rec = testdir.inline_run("-m", opt)
|
||||
passed, skipped, fail = rec.listoutcomes()
|
||||
passed = [x.nodeid.split("::")[-1] for x in passed]
|
||||
assert len(passed) == len(passed_result)
|
||||
assert list(passed) == list(passed_result)
|
||||
|
||||
|
||||
class TestFunctional:
|
||||
|
||||
def test_mark_per_function(self, testdir):
|
||||
|
|
Loading…
Reference in New Issue