Merge pull request #2301 from nicoddemus/merge-master-into-features
Merge master into features
This commit is contained in:
commit
d1ea7c8cc8
45
.travis.yml
45
.travis.yml
|
@ -8,34 +8,37 @@ install: "pip install -U tox"
|
|||
env:
|
||||
matrix:
|
||||
# coveralls is not listed in tox's envlist, but should run in travis
|
||||
- TESTENV=coveralls
|
||||
- TOXENV=coveralls
|
||||
# note: please use "tox --listenvs" to populate the build matrix below
|
||||
- TESTENV=linting
|
||||
- TESTENV=py26
|
||||
- TESTENV=py27
|
||||
- TESTENV=py33
|
||||
- TESTENV=py34
|
||||
- TESTENV=py35
|
||||
- TESTENV=pypy
|
||||
- TESTENV=py27-pexpect
|
||||
- TESTENV=py27-xdist
|
||||
- TESTENV=py27-trial
|
||||
- TESTENV=py35-pexpect
|
||||
- TESTENV=py35-xdist
|
||||
- TESTENV=py35-trial
|
||||
- TESTENV=py27-nobyte
|
||||
- TESTENV=doctesting
|
||||
- TESTENV=freeze
|
||||
- TESTENV=docs
|
||||
- TOXENV=linting
|
||||
- TOXENV=py26
|
||||
- TOXENV=py27
|
||||
- TOXENV=py33
|
||||
- TOXENV=py34
|
||||
- TOXENV=py35
|
||||
- TOXENV=pypy
|
||||
- TOXENV=py27-pexpect
|
||||
- TOXENV=py27-xdist
|
||||
- TOXENV=py27-trial
|
||||
- TOXENV=py35-pexpect
|
||||
- TOXENV=py35-xdist
|
||||
- TOXENV=py35-trial
|
||||
- TOXENV=py27-nobyte
|
||||
- TOXENV=doctesting
|
||||
- TOXENV=freeze
|
||||
- TOXENV=docs
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- env: TESTENV=py36
|
||||
- env: TOXENV=py36
|
||||
python: '3.6-dev'
|
||||
- env: TESTENV=py37
|
||||
- env: TOXENV=py37
|
||||
python: 'nightly'
|
||||
allow_failures:
|
||||
- env: TOXENV=py37
|
||||
python: 'nightly'
|
||||
|
||||
script: tox --recreate -e $TESTENV
|
||||
script: tox --recreate
|
||||
|
||||
notifications:
|
||||
irc:
|
||||
|
|
6
AUTHORS
6
AUTHORS
|
@ -17,6 +17,7 @@ Anthony Sottile
|
|||
Armin Rigo
|
||||
Aron Curzon
|
||||
Aviv Palivoda
|
||||
Barney Gale
|
||||
Ben Webb
|
||||
Benjamin Peterson
|
||||
Bernard Pratz
|
||||
|
@ -43,6 +44,7 @@ Dave Hunt
|
|||
David Díaz-Barquero
|
||||
David Mohr
|
||||
David Vierra
|
||||
Denis Kirisov
|
||||
Diego Russo
|
||||
Dmitry Dygalo
|
||||
Duncan Betts
|
||||
|
@ -117,11 +119,14 @@ Nicolas Delaby
|
|||
Oleg Pidsadnyi
|
||||
Oliver Bestwalter
|
||||
Omar Kohl
|
||||
Omer Hadari
|
||||
Patrick Hayes
|
||||
Pieter Mulder
|
||||
Piotr Banaszkiewicz
|
||||
Punyashloka Biswal
|
||||
Quentin Pradet
|
||||
Ralf Schmitt
|
||||
Ran Benita
|
||||
Raphael Pierzina
|
||||
Raquel Alegre
|
||||
Ravi Chandra
|
||||
|
@ -148,5 +153,6 @@ Tyler Goodlet
|
|||
Vasily Kuznetsov
|
||||
Victor Uriarte
|
||||
Vlad Dragos
|
||||
Vidar T. Fauske
|
||||
Wouter van Ackooy
|
||||
Xuecong Liao
|
||||
|
|
|
@ -27,6 +27,11 @@ Changes
|
|||
``__test__`` attribute to ``False`` in the class body (`#2007`_). Thanks
|
||||
to `@syre`_ for the report and `@lwm`_ for the PR.
|
||||
|
||||
* Change junitxml.py to produce reports that comply with Junitxml schema.
|
||||
If the same test fails with failure in call and then errors in teardown
|
||||
we split testcase element into two, one containing the error and the other
|
||||
the failure. (`#2228`_) Thanks to `@kkoukiou`_ for the PR.
|
||||
|
||||
* Testcase reports with a ``url`` attribute will now properly write this to junitxml.
|
||||
Thanks `@fushi`_ for the PR (`#1874`_).
|
||||
|
||||
|
@ -73,6 +78,7 @@ Bug Fixes
|
|||
.. _@unsignedint: https://github.com/unsignedint
|
||||
.. _@Kriechi: https://github.com/Kriechi
|
||||
|
||||
|
||||
.. _#1407: https://github.com/pytest-dev/pytest/issues/1407
|
||||
.. _#1512: https://github.com/pytest-dev/pytest/issues/1512
|
||||
.. _#1874: https://github.com/pytest-dev/pytest/pull/1874
|
||||
|
@ -83,24 +89,65 @@ Bug Fixes
|
|||
.. _#2166: https://github.com/pytest-dev/pytest/pull/2166
|
||||
.. _#2147: https://github.com/pytest-dev/pytest/issues/2147
|
||||
.. _#2208: https://github.com/pytest-dev/pytest/issues/2208
|
||||
.. _#2228: https://github.com/pytest-dev/pytest/issues/2228
|
||||
|
||||
3.0.7 (unreleased)
|
||||
=======================
|
||||
==================
|
||||
|
||||
* Change junitxml.py to produce reports that comply with Junitxml schema.
|
||||
If the same test fails with failure in call and then errors in teardown
|
||||
we split testcase element into two, one containing the error and the other
|
||||
the failure. (`#2228`_) Thanks to `@kkoukiou`_ for the PR.
|
||||
|
||||
*
|
||||
|
||||
*
|
||||
|
||||
* Fix issue in assertion rewriting breaking due to modules silently discarding
|
||||
other modules when importing fails
|
||||
Notably, importing the `anydbm` module is fixed. (`#2248`_).
|
||||
Thanks `@pfhayes`_ for the PR.
|
||||
|
||||
* junitxml: Fix problematic case where system-out tag occured twice per testcase
|
||||
element in the XML report. Thanks `@kkoukiou`_ for the PR.
|
||||
|
||||
* Fix regression, pytest now skips unittest correctly if run with ``--pdb``
|
||||
(`#2137`_). Thanks to `@gst`_ for the report and `@mbyt`_ for the PR.
|
||||
|
||||
* Ignore exceptions raised from descriptors (e.g. properties) during Python test collection (`#2234`_).
|
||||
Thanks to `@bluetech`_.
|
||||
|
||||
* ``--override-ini`` now correctly overrides some fundamental options like ``python_files`` (`#2238`_).
|
||||
Thanks `@sirex`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
* Replace ``raise StopIteration`` usages in the code by simple ``returns`` to finish generators, in accordance to `PEP-479`_ (`#2160`_).
|
||||
Thanks `@tgoodlet`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
* Fix internal errors when an unprintable ``AssertionError`` is raised inside a test.
|
||||
Thanks `@omerhadari`_ for the PR.
|
||||
|
||||
* Skipping plugin now also works with test items generated by custom collectors (`#2231`_).
|
||||
Thanks to `@vidartf`_.
|
||||
|
||||
* Fix trailing whitespace in console output if no .ini file presented (`#2281`_). Thanks `@fbjorn`_ for the PR.
|
||||
|
||||
* Conditionless ``xfail`` markers no longer rely on the underlying test item
|
||||
being an instance of ``PyobjMixin``, and can therefore apply to tests not
|
||||
collected by the built-in python test collector. Thanks `@barneygale`_ for the
|
||||
PR.
|
||||
|
||||
*
|
||||
|
||||
.. _@pfhayes: https://github.com/pfhayes
|
||||
.. _@bluetech: https://github.com/bluetech
|
||||
.. _@gst: https://github.com/gst
|
||||
.. _@sirex: https://github.com/sirex
|
||||
.. _@vidartf: https://github.com/vidartf
|
||||
.. _@kkoukiou: https://github.com/KKoukiou
|
||||
.. _@omerhadari: https://github.com/omerhadari
|
||||
.. _@fbjorn: https://github.com/fbjorn
|
||||
|
||||
.. _#2228: https://github.com/pytest-dev/pytest/issues/2228
|
||||
.. _#2248: https://github.com/pytest-dev/pytest/issues/2248
|
||||
.. _#2137: https://github.com/pytest-dev/pytest/issues/2137
|
||||
.. _#2160: https://github.com/pytest-dev/pytest/issues/2160
|
||||
.. _#2231: https://github.com/pytest-dev/pytest/issues/2231
|
||||
.. _#2234: https://github.com/pytest-dev/pytest/issues/2234
|
||||
.. _#2238: https://github.com/pytest-dev/pytest/issues/2238
|
||||
.. _#2281: https://github.com/pytest-dev/pytest/issues/2281
|
||||
|
||||
.. _PEP-479: https://www.python.org/dev/peps/pep-0479/
|
||||
|
||||
|
||||
3.0.6 (2017-01-22)
|
||||
|
@ -135,6 +182,7 @@ Bug Fixes
|
|||
terminal output it relies on is missing. Thanks to `@eli-b`_ for the PR.
|
||||
|
||||
|
||||
.. _@barneygale: https://github.com/barneygale
|
||||
.. _@lesteve: https://github.com/lesteve
|
||||
.. _@malinoff: https://github.com/malinoff
|
||||
.. _@pelme: https://github.com/pelme
|
||||
|
@ -2467,7 +2515,7 @@ Bug fixes:
|
|||
teardown function are called earlier.
|
||||
- add an all-powerful metafunc.parametrize function which allows to
|
||||
parametrize test function arguments in multiple steps and therefore
|
||||
from indepdenent plugins and palces.
|
||||
from independent plugins and places.
|
||||
- add a @pytest.mark.parametrize helper which allows to easily
|
||||
call a test function with different argument values
|
||||
- Add examples to the "parametrize" example page, including a quick port
|
||||
|
|
|
@ -352,6 +352,8 @@ class ExceptionInfo(object):
|
|||
help for navigating the traceback.
|
||||
"""
|
||||
_striptext = ''
|
||||
_assert_start_repr = "AssertionError(u\'assert " if sys.version_info[0] < 3 else "AssertionError(\'assert "
|
||||
|
||||
def __init__(self, tup=None, exprinfo=None):
|
||||
import _pytest._code
|
||||
if tup is None:
|
||||
|
@ -359,8 +361,8 @@ class ExceptionInfo(object):
|
|||
if exprinfo is None and isinstance(tup[1], AssertionError):
|
||||
exprinfo = getattr(tup[1], 'msg', None)
|
||||
if exprinfo is None:
|
||||
exprinfo = py._builtin._totext(tup[1])
|
||||
if exprinfo and exprinfo.startswith('assert '):
|
||||
exprinfo = py.io.saferepr(tup[1])
|
||||
if exprinfo and exprinfo.startswith(self._assert_start_repr):
|
||||
self._striptext = 'AssertionError: '
|
||||
self._excinfo = tup
|
||||
#: the exception class
|
||||
|
|
|
@ -215,7 +215,8 @@ class AssertionRewritingHook(object):
|
|||
mod.__loader__ = self
|
||||
py.builtin.exec_(co, mod.__dict__)
|
||||
except:
|
||||
del sys.modules[name]
|
||||
if name in sys.modules:
|
||||
del sys.modules[name]
|
||||
raise
|
||||
return sys.modules[name]
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""
|
||||
merged implementation of the cache provider
|
||||
|
||||
the name cache was not choosen to ensure pluggy automatically
|
||||
the name cache was not chosen to ensure pluggy automatically
|
||||
ignores the external pytest-cache
|
||||
"""
|
||||
|
||||
|
|
|
@ -877,6 +877,7 @@ class Config(object):
|
|||
self.trace = self.pluginmanager.trace.root.get("config")
|
||||
self.hook = self.pluginmanager.hook
|
||||
self._inicache = {}
|
||||
self._override_ini = ()
|
||||
self._opt2dest = {}
|
||||
self._cleanup = []
|
||||
self._warn = self.pluginmanager._warn
|
||||
|
@ -977,6 +978,7 @@ class Config(object):
|
|||
self.invocation_dir = py.path.local()
|
||||
self._parser.addini('addopts', 'extra command line options', 'args')
|
||||
self._parser.addini('minversion', 'minimally required pytest version')
|
||||
self._override_ini = ns.override_ini or ()
|
||||
|
||||
def _consider_importhook(self, args, entrypoint_name):
|
||||
"""Install the PEP 302 import hook if using assertion re-writing.
|
||||
|
@ -1159,15 +1161,14 @@ class Config(object):
|
|||
# and -o foo1=bar1 -o foo2=bar2 options
|
||||
# always use the last item if multiple value set for same ini-name,
|
||||
# e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2
|
||||
if self.getoption("override_ini", None):
|
||||
for ini_config_list in self.option.override_ini:
|
||||
for ini_config in ini_config_list:
|
||||
try:
|
||||
(key, user_ini_value) = ini_config.split("=", 1)
|
||||
except ValueError:
|
||||
raise UsageError("-o/--override-ini expects option=value style.")
|
||||
if key == name:
|
||||
value = user_ini_value
|
||||
for ini_config_list in self._override_ini:
|
||||
for ini_config in ini_config_list:
|
||||
try:
|
||||
(key, user_ini_value) = ini_config.split("=", 1)
|
||||
except ValueError:
|
||||
raise UsageError("-o/--override-ini expects option=value style.")
|
||||
if key == name:
|
||||
value = user_ini_value
|
||||
return value
|
||||
|
||||
def getoption(self, name, default=notset, skip=False):
|
||||
|
|
|
@ -14,6 +14,7 @@ from _pytest.compat import (
|
|||
getfslineno, get_real_func,
|
||||
is_generator, isclass, getimfunc,
|
||||
getlocation, getfuncargnames,
|
||||
safe_getattr,
|
||||
)
|
||||
|
||||
def pytest_sessionstart(session):
|
||||
|
@ -124,8 +125,6 @@ def getfixturemarker(obj):
|
|||
exceptions."""
|
||||
try:
|
||||
return getattr(obj, "_pytestfixturefunction", None)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except Exception:
|
||||
# some objects raise errors like request (from flask import request)
|
||||
# we don't expect them to be fixture functions
|
||||
|
@ -1068,7 +1067,9 @@ class FixtureManager(object):
|
|||
self._holderobjseen.add(holderobj)
|
||||
autousenames = []
|
||||
for name in dir(holderobj):
|
||||
obj = getattr(holderobj, name, None)
|
||||
# The attribute can be an arbitrary descriptor, so the attribute
|
||||
# access below can raise. safe_getatt() ignores such exceptions.
|
||||
obj = safe_getattr(holderobj, name, None)
|
||||
# fixture functions have a pytest_funcarg__ prefix (pre-2.3 style)
|
||||
# or are "@pytest.fixture" marked
|
||||
marker = getfixturemarker(obj)
|
||||
|
|
|
@ -247,7 +247,7 @@ def pytest_unconfigure(config):
|
|||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# hooks for customising the assert methods
|
||||
# hooks for customizing the assert methods
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def pytest_assertrepr_compare(config, op, left, right):
|
||||
|
@ -256,7 +256,7 @@ def pytest_assertrepr_compare(config, op, left, right):
|
|||
Return None for no custom explanation, otherwise return a list
|
||||
of strings. The strings will be joined by newlines but any newlines
|
||||
*in* a string will be escaped. Note that all but the first line will
|
||||
be indented sligthly, the intention is for the first line to be a summary.
|
||||
be indented slightly, the intention is for the first line to be a summary.
|
||||
"""
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
@ -264,7 +264,14 @@ def pytest_assertrepr_compare(config, op, left, right):
|
|||
# -------------------------------------------------------------------------
|
||||
|
||||
def pytest_report_header(config, startdir):
|
||||
""" return a string to be displayed as header info for terminal reporting."""
|
||||
""" return a string to be displayed as header info for terminal reporting.
|
||||
|
||||
.. note::
|
||||
|
||||
This function should be implemented only in plugins or ``conftest.py``
|
||||
files situated at the tests root directory due to how pytest
|
||||
:ref:`discovers plugins during startup <pluginorder>`.
|
||||
"""
|
||||
|
||||
@hookspec(firstresult=True)
|
||||
def pytest_report_teststatus(report):
|
||||
|
|
|
@ -121,7 +121,7 @@ class _NodeReporter(object):
|
|||
node = kind(data, message=message)
|
||||
self.append(node)
|
||||
|
||||
def _write_captured_output(self, report):
|
||||
def write_captured_output(self, report):
|
||||
for capname in ('out', 'err'):
|
||||
content = getattr(report, 'capstd' + capname)
|
||||
if content:
|
||||
|
@ -130,7 +130,6 @@ class _NodeReporter(object):
|
|||
|
||||
def append_pass(self, report):
|
||||
self.add_stats('passed')
|
||||
self._write_captured_output(report)
|
||||
|
||||
def append_failure(self, report):
|
||||
# msg = str(report.longrepr.reprtraceback.extraline)
|
||||
|
@ -149,7 +148,6 @@ class _NodeReporter(object):
|
|||
fail = Junit.failure(message=message)
|
||||
fail.append(bin_xml_escape(report.longrepr))
|
||||
self.append(fail)
|
||||
self._write_captured_output(report)
|
||||
|
||||
def append_collect_error(self, report):
|
||||
# msg = str(report.longrepr.reprtraceback.extraline)
|
||||
|
@ -167,7 +165,6 @@ class _NodeReporter(object):
|
|||
msg = "test setup failure"
|
||||
self._add_simple(
|
||||
Junit.error, msg, report.longrepr)
|
||||
self._write_captured_output(report)
|
||||
|
||||
def append_skipped(self, report):
|
||||
if hasattr(report, "wasxfail"):
|
||||
|
@ -182,7 +179,7 @@ class _NodeReporter(object):
|
|||
Junit.skipped("%s:%s: %s" % (filename, lineno, skipreason),
|
||||
type="pytest.skip",
|
||||
message=skipreason))
|
||||
self._write_captured_output(report)
|
||||
self.write_captured_output(report)
|
||||
|
||||
def finalize(self):
|
||||
data = self.to_xml().unicode(indent=0)
|
||||
|
@ -369,6 +366,8 @@ class LogXML(object):
|
|||
reporter.append_skipped(report)
|
||||
self.update_testcase_duration(report)
|
||||
if report.when == "teardown":
|
||||
reporter = self._opentestcase(report)
|
||||
reporter.write_captured_output(report)
|
||||
self.finalize(report)
|
||||
report_wid = getattr(report, "worker_id", None)
|
||||
report_ii = getattr(report, "item_index", None)
|
||||
|
|
|
@ -81,7 +81,7 @@ def pytest_namespace():
|
|||
|
||||
|
||||
def pytest_configure(config):
|
||||
pytest.config = config # compatibiltiy
|
||||
pytest.config = config # compatibility
|
||||
|
||||
|
||||
def wrap_session(config, doit):
|
||||
|
|
|
@ -72,7 +72,7 @@ def pytest_collection_modifyitems(items, config):
|
|||
return
|
||||
# pytest used to allow "-" for negating
|
||||
# but today we just allow "-" at the beginning, use "not" instead
|
||||
# we probably remove "-" alltogether soon
|
||||
# we probably remove "-" altogether soon
|
||||
if keywordexpr.startswith("-"):
|
||||
keywordexpr = "not " + keywordexpr[1:]
|
||||
selectuntil = False
|
||||
|
|
|
@ -335,7 +335,7 @@ def testdir(request, tmpdir_factory):
|
|||
return Testdir(request, tmpdir_factory)
|
||||
|
||||
|
||||
rex_outcome = re.compile("(\d+) ([\w-]+)")
|
||||
rex_outcome = re.compile(r"(\d+) ([\w-]+)")
|
||||
class RunResult(object):
|
||||
"""The result of running a command.
|
||||
|
||||
|
@ -570,7 +570,7 @@ class Testdir(object):
|
|||
def mkpydir(self, name):
|
||||
"""Create a new python package.
|
||||
|
||||
This creates a (sub)direcotry with an empty ``__init__.py``
|
||||
This creates a (sub)directory with an empty ``__init__.py``
|
||||
file so that is recognised as a python package.
|
||||
|
||||
"""
|
||||
|
@ -665,7 +665,7 @@ class Testdir(object):
|
|||
def inline_genitems(self, *args):
|
||||
"""Run ``pytest.main(['--collectonly'])`` in-process.
|
||||
|
||||
Retuns a tuple of the collected items and a
|
||||
Returns a tuple of the collected items and a
|
||||
:py:class:`HookRecorder` instance.
|
||||
|
||||
This runs the :py:func:`pytest.main` function to run all of
|
||||
|
@ -863,7 +863,7 @@ class Testdir(object):
|
|||
:py:meth:`parseconfigure`.
|
||||
|
||||
:param withinit: Whether to also write a ``__init__.py`` file
|
||||
to the temporarly directory to ensure it is a package.
|
||||
to the temporary directory to ensure it is a package.
|
||||
|
||||
"""
|
||||
kw = {self.request.function.__name__: Source(source).strip()}
|
||||
|
|
|
@ -174,7 +174,7 @@ def pytest_pycollect_makeitem(collector, name, obj):
|
|||
outcome = yield
|
||||
res = outcome.get_result()
|
||||
if res is not None:
|
||||
raise StopIteration
|
||||
return
|
||||
# nothing was collected elsewhere, let's do it here
|
||||
if isclass(obj):
|
||||
if collector.istestclass(obj, name):
|
||||
|
@ -632,7 +632,7 @@ class Generator(FunctionMixin, PyCollector):
|
|||
def getcallargs(self, obj):
|
||||
if not isinstance(obj, (tuple, list)):
|
||||
obj = (obj,)
|
||||
# explict naming
|
||||
# explicit naming
|
||||
if isinstance(obj[0], py.builtin._basestring):
|
||||
name = obj[0]
|
||||
obj = obj[1:]
|
||||
|
|
|
@ -112,14 +112,14 @@ class MarkEvaluator(object):
|
|||
|
||||
def _getglobals(self):
|
||||
d = {'os': os, 'sys': sys, 'config': self.item.config}
|
||||
d.update(self.item.obj.__globals__)
|
||||
if hasattr(self.item, 'obj'):
|
||||
d.update(self.item.obj.__globals__)
|
||||
return d
|
||||
|
||||
def _istrue(self):
|
||||
if hasattr(self, 'result'):
|
||||
return self.result
|
||||
if self.holder:
|
||||
d = self._getglobals()
|
||||
if self.holder.args or 'condition' in self.holder.kwargs:
|
||||
self.result = False
|
||||
# "holder" might be a MarkInfo or a MarkDecorator; only
|
||||
|
@ -133,6 +133,7 @@ class MarkEvaluator(object):
|
|||
for expr in args:
|
||||
self.expr = expr
|
||||
if isinstance(expr, py.builtin._basestring):
|
||||
d = self._getglobals()
|
||||
result = cached_eval(self.item.config, expr, d)
|
||||
else:
|
||||
if "reason" not in kwargs:
|
||||
|
|
|
@ -295,8 +295,8 @@ class TerminalReporter(object):
|
|||
def pytest_report_header(self, config):
|
||||
inifile = ""
|
||||
if config.inifile:
|
||||
inifile = config.rootdir.bestrelpath(config.inifile)
|
||||
lines = ["rootdir: %s, inifile: %s" %(config.rootdir, inifile)]
|
||||
inifile = " " + config.rootdir.bestrelpath(config.inifile)
|
||||
lines = ["rootdir: %s, inifile:%s" % (config.rootdir, inifile)]
|
||||
|
||||
plugininfo = config.pluginmanager.list_plugin_distinfo()
|
||||
if plugininfo:
|
||||
|
|
|
@ -116,7 +116,7 @@ def tmpdir(request, tmpdir_factory):
|
|||
path object.
|
||||
"""
|
||||
name = request.node.name
|
||||
name = re.sub("[\W]", "_", name)
|
||||
name = re.sub(r"[\W]", "_", name)
|
||||
MAXVAL = 30
|
||||
if len(name) > MAXVAL:
|
||||
name = name[:MAXVAL]
|
||||
|
|
|
@ -5,7 +5,7 @@ import sys
|
|||
import traceback
|
||||
|
||||
import pytest
|
||||
# for transfering markers
|
||||
# for transferring markers
|
||||
import _pytest._code
|
||||
from _pytest.python import transfer_markers
|
||||
from _pytest.skipping import MarkEvaluator
|
||||
|
@ -65,7 +65,6 @@ class UnitTestCase(pytest.Class):
|
|||
yield TestCaseFunction('runTest', parent=self)
|
||||
|
||||
|
||||
|
||||
class TestCaseFunction(pytest.Function):
|
||||
_excinfo = None
|
||||
|
||||
|
@ -152,14 +151,33 @@ class TestCaseFunction(pytest.Function):
|
|||
def stopTest(self, testcase):
|
||||
pass
|
||||
|
||||
def _handle_skip(self):
|
||||
# implements the skipping machinery (see #2137)
|
||||
# analog to pythons Lib/unittest/case.py:run
|
||||
testMethod = getattr(self._testcase, self._testcase._testMethodName)
|
||||
if (getattr(self._testcase.__class__, "__unittest_skip__", False) or
|
||||
getattr(testMethod, "__unittest_skip__", False)):
|
||||
# If the class or method was skipped.
|
||||
skip_why = (getattr(self._testcase.__class__, '__unittest_skip_why__', '') or
|
||||
getattr(testMethod, '__unittest_skip_why__', ''))
|
||||
try: # PY3, unittest2 on PY2
|
||||
self._testcase._addSkip(self, self._testcase, skip_why)
|
||||
except TypeError: # PY2
|
||||
if sys.version_info[0] != 2:
|
||||
raise
|
||||
self._testcase._addSkip(self, skip_why)
|
||||
return True
|
||||
return False
|
||||
|
||||
def runtest(self):
|
||||
if self.config.pluginmanager.get_plugin("pdbinvoke") is None:
|
||||
self._testcase(result=self)
|
||||
else:
|
||||
# disables tearDown and cleanups for post mortem debugging (see #1890)
|
||||
if self._handle_skip():
|
||||
return
|
||||
self._testcase.debug()
|
||||
|
||||
|
||||
def _prunetraceback(self, excinfo):
|
||||
pytest.Function._prunetraceback(self, excinfo)
|
||||
traceback = excinfo.traceback.filter(
|
||||
|
|
|
@ -287,5 +287,5 @@ For further information, Benjamin Peterson wrote up `Behind the scenes of pytest
|
|||
``--nomagic``.
|
||||
|
||||
.. versionchanged:: 3.0
|
||||
Removes the ``--no-assert`` and``--nomagic`` options.
|
||||
Removes the ``--no-assert`` and ``--nomagic`` options.
|
||||
Removes the ``--assert=reinterp`` option.
|
||||
|
|
|
@ -243,7 +243,9 @@ Fixture finalization / executing teardown code
|
|||
|
||||
pytest supports execution of fixture specific finalization code
|
||||
when the fixture goes out of scope. By using a ``yield`` statement instead of ``return``, all
|
||||
the code after the *yield* statement serves as the teardown code.::
|
||||
the code after the *yield* statement serves as the teardown code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
|
@ -275,22 +277,23 @@ occur around each single test. In either case the test
|
|||
module itself does not need to change or know about these details
|
||||
of fixture setup.
|
||||
|
||||
Note that we can also seamlessly use the ``yield`` syntax with ``with`` statements::
|
||||
Note that we can also seamlessly use the ``yield`` syntax with ``with`` statements:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_yield2.py
|
||||
|
||||
import smtplib
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def passwd():
|
||||
with open("/etc/passwd") as f:
|
||||
yield f.readlines()
|
||||
@pytest.fixture(scope="module")
|
||||
def smtp(request):
|
||||
with smtplib.SMTP("smtp.gmail.com") as smtp:
|
||||
yield smtp # provide the fixture value
|
||||
|
||||
def test_has_lines(passwd):
|
||||
assert len(passwd) >= 1
|
||||
|
||||
The file ``f`` will be closed after the test finished execution
|
||||
because the Python ``file`` object supports finalization when
|
||||
The ``smtp`` connection will be closed after the test finished execution
|
||||
because the ``smtp`` object automatically closes when
|
||||
the ``with`` statement ends.
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,9 @@ Talks and Tutorials
|
|||
|
||||
.. sidebar:: Next Open Trainings
|
||||
|
||||
`pytest workshop <http://www.meetup.com/Python-Django-User-Group-Bern/events/235151115/>`_, 8th December 2016, Bern, Switzerland
|
||||
`Professional Testing with Python
|
||||
<http://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_,
|
||||
26-28 April 2017, Leipzig, Germany.
|
||||
|
||||
.. _`funcargs`: funcargs.html
|
||||
|
||||
|
|
|
@ -236,20 +236,31 @@ import ``helper.py`` normally. The contents of
|
|||
Requiring/Loading plugins in a test module or conftest file
|
||||
-----------------------------------------------------------
|
||||
|
||||
You can require plugins in a test module or a conftest file like this::
|
||||
You can require plugins in a test module or a ``conftest.py`` file like this:
|
||||
|
||||
pytest_plugins = "name1", "name2",
|
||||
.. code-block:: python
|
||||
|
||||
pytest_plugins = ["name1", "name2"]
|
||||
|
||||
When the test module or conftest plugin is loaded the specified plugins
|
||||
will be loaded as well. You can also use dotted path like this::
|
||||
will be loaded as well. Any module can be blessed as a plugin, including internal
|
||||
application modules:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pytest_plugins = "myapp.testsupport.myplugin"
|
||||
|
||||
which will import the specified module as a ``pytest`` plugin.
|
||||
``pytest_plugins`` variables are processed recursively, so note that in the example above
|
||||
if ``myapp.testsupport.myplugin`` also declares ``pytest_plugins``, the contents
|
||||
of the variable will also be loaded as plugins, and so on.
|
||||
|
||||
Plugins imported like this will automatically be marked to require
|
||||
assertion rewriting using the :func:`pytest.register_assert_rewrite`
|
||||
mechanism. However for this to have any effect the module must not be
|
||||
This mechanism makes it easy to share fixtures within applications or even
|
||||
external applications without the need to create external plugins using
|
||||
the ``setuptools``'s entry point technique.
|
||||
|
||||
Plugins imported by ``pytest_plugins`` will also automatically be marked
|
||||
for assertion rewriting (see :func:`pytest.register_assert_rewrite`).
|
||||
However for this to have any effect the module must not be
|
||||
imported already; if it was already imported at the time the
|
||||
``pytest_plugins`` statement is processed, a warning will result and
|
||||
assertions inside the plugin will not be re-written. To fix this you
|
||||
|
|
|
@ -166,6 +166,16 @@ class TestClass(object):
|
|||
"because it has a __new__ constructor*"
|
||||
)
|
||||
|
||||
def test_issue2234_property(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
class TestCase(object):
|
||||
@property
|
||||
def prop(self):
|
||||
raise NotImplementedError()
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
assert result.ret == EXIT_NOTESTSCOLLECTED
|
||||
|
||||
|
||||
class TestGenerator(object):
|
||||
def test_generative_functions(self, testdir):
|
||||
|
|
|
@ -365,7 +365,7 @@ class TestAssert_reprcompare(object):
|
|||
expl = '\n'.join(callequal(left, right, verbose=True))
|
||||
assert expl.endswith(textwrap.dedent(expected).strip())
|
||||
|
||||
def test_list_different_lenghts(self):
|
||||
def test_list_different_lengths(self):
|
||||
expl = callequal([0, 1], [0, 1, 2])
|
||||
assert len(expl) > 1
|
||||
expl = callequal([0, 1, 2], [0, 1])
|
||||
|
@ -996,6 +996,25 @@ def test_assert_with_unicode(monkeypatch, testdir):
|
|||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(['*AssertionError*'])
|
||||
|
||||
def test_raise_unprintable_assertion_error(testdir):
|
||||
testdir.makepyfile(r"""
|
||||
def test_raise_assertion_error():
|
||||
raise AssertionError('\xff')
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines([r"> raise AssertionError('\xff')", 'E AssertionError: *'])
|
||||
|
||||
def test_raise_assertion_error_raisin_repr(testdir):
|
||||
testdir.makepyfile(u"""
|
||||
class RaisingRepr(object):
|
||||
def __repr__(self):
|
||||
raise Exception()
|
||||
def test_raising_repr():
|
||||
raise AssertionError(RaisingRepr())
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(['E AssertionError: <unprintable AssertionError object>'])
|
||||
|
||||
def test_issue_1944(testdir):
|
||||
testdir.makepyfile("""
|
||||
def f():
|
||||
|
|
|
@ -271,7 +271,7 @@ class TestAssertionRewrite(object):
|
|||
|
||||
getmsg(f, must_pass=True)
|
||||
|
||||
def test_short_circut_evaluation(self):
|
||||
def test_short_circuit_evaluation(self):
|
||||
def f():
|
||||
assert True or explode # noqa
|
||||
|
||||
|
|
|
@ -604,7 +604,7 @@ def test_capture_binary_output(testdir):
|
|||
|
||||
|
||||
def test_error_during_readouterr(testdir):
|
||||
"""Make sure we suspend capturing if errors occurr during readouterr"""
|
||||
"""Make sure we suspend capturing if errors occur during readouterr"""
|
||||
testdir.makepyfile(pytest_xyz="""
|
||||
from _pytest.capture import FDCapture
|
||||
def bad_snap(self):
|
||||
|
|
|
@ -519,7 +519,7 @@ def test_consider_args_after_options_for_rootdir_and_inifile(testdir, args):
|
|||
args[i] = d2
|
||||
with root.as_cwd():
|
||||
result = testdir.runpytest(*args)
|
||||
result.stdout.fnmatch_lines(['*rootdir: *myroot, inifile: '])
|
||||
result.stdout.fnmatch_lines(['*rootdir: *myroot, inifile:'])
|
||||
|
||||
|
||||
@pytest.mark.skipif("sys.platform == 'win32'")
|
||||
|
@ -778,6 +778,21 @@ class TestOverrideIniArgs(object):
|
|||
result = testdir.runpytest("--override-ini", 'xdist_strict True', "-s")
|
||||
result.stderr.fnmatch_lines(["*ERROR* *expects option=value*"])
|
||||
|
||||
@pytest.mark.parametrize('with_ini', [True, False])
|
||||
def test_override_ini_handled_asap(self, testdir, with_ini):
|
||||
"""-o should be handled as soon as possible and always override what's in ini files (#2238)"""
|
||||
if with_ini:
|
||||
testdir.makeini("""
|
||||
[pytest]
|
||||
python_files=test_*.py
|
||||
""")
|
||||
testdir.makepyfile(unittest_ini_handle="""
|
||||
def test():
|
||||
pass
|
||||
""")
|
||||
result = testdir.runpytest("--override-ini", 'python_files=unittest_*.py')
|
||||
result.stdout.fnmatch_lines(["*1 passed in*"])
|
||||
|
||||
def test_with_arg_outside_cwd_without_inifile(self, tmpdir, monkeypatch):
|
||||
monkeypatch.chdir(str(tmpdir))
|
||||
a = tmpdir.mkdir("a")
|
||||
|
|
|
@ -580,6 +580,25 @@ class TestPython(object):
|
|||
systemout = pnode.find_first_by_tag("system-err")
|
||||
assert "hello-stderr" in systemout.toxml()
|
||||
|
||||
def test_avoid_double_stdout(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def arg(request):
|
||||
yield
|
||||
sys.stdout.write('hello-stdout teardown')
|
||||
raise ValueError()
|
||||
def test_function(arg):
|
||||
sys.stdout.write('hello-stdout call')
|
||||
""")
|
||||
result, dom = runandparse(testdir)
|
||||
node = dom.find_first_by_tag("testsuite")
|
||||
pnode = node.find_first_by_tag("testcase")
|
||||
systemout = pnode.find_first_by_tag("system-out")
|
||||
assert "hello-stdout call" in systemout.toxml()
|
||||
assert "hello-stdout teardown" in systemout.toxml()
|
||||
|
||||
def test_mangle_test_address():
|
||||
from _pytest.junitxml import mangle_test_address
|
||||
|
|
|
@ -126,6 +126,21 @@ class TestPDB(object):
|
|||
assert 'debug.me' in rest
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_unittest_skip(self, testdir):
|
||||
"""Test for issue #2137"""
|
||||
p1 = testdir.makepyfile("""
|
||||
import unittest
|
||||
@unittest.skipIf(True, 'Skipping also with pdb active')
|
||||
class MyTestCase(unittest.TestCase):
|
||||
def test_one(self):
|
||||
assert 0
|
||||
""")
|
||||
child = testdir.spawn_pytest("-rs --pdb %s" % p1)
|
||||
child.expect('Skipping also with pdb active')
|
||||
child.expect('1 skipped in')
|
||||
child.sendeof()
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_interaction_capture(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
def test_1():
|
||||
|
|
|
@ -197,7 +197,7 @@ class TestNewSession(SessionTests):
|
|||
colfail = [x for x in finished if x.failed]
|
||||
assert len(colfail) == 1
|
||||
|
||||
def test_minus_x_overriden_by_maxfail(self, testdir):
|
||||
def test_minus_x_overridden_by_maxfail(self, testdir):
|
||||
testdir.makepyfile(__init__="")
|
||||
testdir.makepyfile(test_one="xxxx", test_two="yyyy", test_third="zzz")
|
||||
reprec = testdir.inline_run("-x", "--maxfail=2", testdir.tmpdir)
|
||||
|
|
|
@ -969,3 +969,26 @@ def test_module_level_skip_error(testdir):
|
|||
result.stdout.fnmatch_lines(
|
||||
"*Using pytest.skip outside of a test is not allowed*"
|
||||
)
|
||||
|
||||
|
||||
def test_mark_xfail_item(testdir):
|
||||
# Ensure pytest.mark.xfail works with non-Python Item
|
||||
testdir.makeconftest("""
|
||||
import pytest
|
||||
|
||||
class MyItem(pytest.Item):
|
||||
nodeid = 'foo'
|
||||
def setup(self):
|
||||
marker = pytest.mark.xfail(True, reason="Expected failure")
|
||||
self.add_marker(marker)
|
||||
def runtest(self):
|
||||
assert False
|
||||
|
||||
def pytest_collect_file(path, parent):
|
||||
return MyItem("foo", parent)
|
||||
""")
|
||||
result = testdir.inline_run()
|
||||
passed, skipped, failed = result.listoutcomes()
|
||||
assert not failed
|
||||
xfailed = [r for r in skipped if hasattr(r, 'wasxfail')]
|
||||
assert xfailed
|
||||
|
|
|
@ -906,3 +906,12 @@ def test_summary_stats(exp_line, exp_color, stats_arg):
|
|||
print("Actually got: \"%s\"; with color \"%s\"" % (line, color))
|
||||
assert line == exp_line
|
||||
assert color == exp_color
|
||||
|
||||
|
||||
def test_no_trailing_whitespace_after_inifile_word(testdir):
|
||||
result = testdir.runpytest('')
|
||||
assert 'inifile:\n' in result.stdout.str()
|
||||
|
||||
testdir.makeini('[pytest]')
|
||||
result = testdir.runpytest('')
|
||||
assert 'inifile: tox.ini\n' in result.stdout.str()
|
||||
|
|
Loading…
Reference in New Issue