Merge branch 'master' into anydbmfix
This commit is contained in:
commit
3aac3d0a00
42
.travis.yml
42
.travis.yml
|
@ -8,34 +8,34 @@ install: "pip install -U tox"
|
||||||
env:
|
env:
|
||||||
matrix:
|
matrix:
|
||||||
# coveralls is not listed in tox's envlist, but should run in travis
|
# 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
|
# note: please use "tox --listenvs" to populate the build matrix below
|
||||||
- TESTENV=linting
|
- TOXENV=linting
|
||||||
- TESTENV=py26
|
- TOXENV=py26
|
||||||
- TESTENV=py27
|
- TOXENV=py27
|
||||||
- TESTENV=py33
|
- TOXENV=py33
|
||||||
- TESTENV=py34
|
- TOXENV=py34
|
||||||
- TESTENV=py35
|
- TOXENV=py35
|
||||||
- TESTENV=pypy
|
- TOXENV=pypy
|
||||||
- TESTENV=py27-pexpect
|
- TOXENV=py27-pexpect
|
||||||
- TESTENV=py27-xdist
|
- TOXENV=py27-xdist
|
||||||
- TESTENV=py27-trial
|
- TOXENV=py27-trial
|
||||||
- TESTENV=py35-pexpect
|
- TOXENV=py35-pexpect
|
||||||
- TESTENV=py35-xdist
|
- TOXENV=py35-xdist
|
||||||
- TESTENV=py35-trial
|
- TOXENV=py35-trial
|
||||||
- TESTENV=py27-nobyte
|
- TOXENV=py27-nobyte
|
||||||
- TESTENV=doctesting
|
- TOXENV=doctesting
|
||||||
- TESTENV=freeze
|
- TOXENV=freeze
|
||||||
- TESTENV=docs
|
- TOXENV=docs
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- env: TESTENV=py36
|
- env: TOXENV=py36
|
||||||
python: '3.6-dev'
|
python: '3.6-dev'
|
||||||
- env: TESTENV=py37
|
- env: TOXENV=py37
|
||||||
python: 'nightly'
|
python: 'nightly'
|
||||||
|
|
||||||
script: tox --recreate -e $TESTENV
|
script: tox --recreate
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
irc:
|
irc:
|
||||||
|
|
|
@ -1,18 +1,23 @@
|
||||||
3.0.7 (unreleased)
|
3.0.7 (unreleased)
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
|
||||||
* Fix issue in assertion rewriting breaking due to modules silently discarding
|
* Fix issue in assertion rewriting breaking due to modules silently discarding
|
||||||
other modules when importing fails
|
other modules when importing fails
|
||||||
Notably, importing the `anydbm` module is fixed. (`#2248`_).
|
Notably, importing the `anydbm` module is fixed. (`#2248`_).
|
||||||
Thanks `@pfhayes`_ for the PR.
|
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``
|
* Fix regression, pytest now skips unittest correctly if run with ``--pdb``
|
||||||
(`#2137`_). Thanks to `@gst`_ for the report and `@mbyt`_ for the PR.
|
(`#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`_).
|
* Ignore exceptions raised from descriptors (e.g. properties) during Python test collection (`#2234`_).
|
||||||
Thanks to `@bluetech`_.
|
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`_).
|
* 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.
|
Thanks `@tgoodlet`_ for the report and `@nicoddemus`_ for the PR.
|
||||||
|
@ -34,13 +39,16 @@
|
||||||
.. _@pfhayes: https://github.com/pfhayes
|
.. _@pfhayes: https://github.com/pfhayes
|
||||||
.. _@bluetech: https://github.com/bluetech
|
.. _@bluetech: https://github.com/bluetech
|
||||||
.. _@gst: https://github.com/gst
|
.. _@gst: https://github.com/gst
|
||||||
|
.. _@sirex: https://github.com/sirex
|
||||||
.. _@vidartf: https://github.com/vidartf
|
.. _@vidartf: https://github.com/vidartf
|
||||||
|
.. _@kkoukiou: https://github.com/KKoukiou
|
||||||
|
|
||||||
.. _#2248: https://github.com/pytest-dev/pytest/issues/2248
|
.. _#2248: https://github.com/pytest-dev/pytest/issues/2248
|
||||||
.. _#2137: https://github.com/pytest-dev/pytest/issues/2137
|
.. _#2137: https://github.com/pytest-dev/pytest/issues/2137
|
||||||
.. _#2160: https://github.com/pytest-dev/pytest/issues/2160
|
.. _#2160: https://github.com/pytest-dev/pytest/issues/2160
|
||||||
.. _#2231: https://github.com/pytest-dev/pytest/issues/2231
|
.. _#2231: https://github.com/pytest-dev/pytest/issues/2231
|
||||||
.. _#2234: https://github.com/pytest-dev/pytest/issues/2234
|
.. _#2234: https://github.com/pytest-dev/pytest/issues/2234
|
||||||
|
.. _#2238: https://github.com/pytest-dev/pytest/issues/2238
|
||||||
|
|
||||||
.. _PEP-479: https://www.python.org/dev/peps/pep-0479/
|
.. _PEP-479: https://www.python.org/dev/peps/pep-0479/
|
||||||
|
|
||||||
|
@ -2408,7 +2416,7 @@ Bug fixes:
|
||||||
teardown function are called earlier.
|
teardown function are called earlier.
|
||||||
- add an all-powerful metafunc.parametrize function which allows to
|
- add an all-powerful metafunc.parametrize function which allows to
|
||||||
parametrize test function arguments in multiple steps and therefore
|
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
|
- add a @pytest.mark.parametrize helper which allows to easily
|
||||||
call a test function with different argument values
|
call a test function with different argument values
|
||||||
- Add examples to the "parametrize" example page, including a quick port
|
- Add examples to the "parametrize" example page, including a quick port
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""
|
"""
|
||||||
merged implementation of the cache provider
|
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
|
ignores the external pytest-cache
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
@ -877,6 +877,7 @@ class Config(object):
|
||||||
self.trace = self.pluginmanager.trace.root.get("config")
|
self.trace = self.pluginmanager.trace.root.get("config")
|
||||||
self.hook = self.pluginmanager.hook
|
self.hook = self.pluginmanager.hook
|
||||||
self._inicache = {}
|
self._inicache = {}
|
||||||
|
self._override_ini = ()
|
||||||
self._opt2dest = {}
|
self._opt2dest = {}
|
||||||
self._cleanup = []
|
self._cleanup = []
|
||||||
self._warn = self.pluginmanager._warn
|
self._warn = self.pluginmanager._warn
|
||||||
|
@ -977,6 +978,7 @@ class Config(object):
|
||||||
self.invocation_dir = py.path.local()
|
self.invocation_dir = py.path.local()
|
||||||
self._parser.addini('addopts', 'extra command line options', 'args')
|
self._parser.addini('addopts', 'extra command line options', 'args')
|
||||||
self._parser.addini('minversion', 'minimally required pytest version')
|
self._parser.addini('minversion', 'minimally required pytest version')
|
||||||
|
self._override_ini = ns.override_ini or ()
|
||||||
|
|
||||||
def _consider_importhook(self, args, entrypoint_name):
|
def _consider_importhook(self, args, entrypoint_name):
|
||||||
"""Install the PEP 302 import hook if using assertion re-writing.
|
"""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
|
# and -o foo1=bar1 -o foo2=bar2 options
|
||||||
# always use the last item if multiple value set for same ini-name,
|
# 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
|
# 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._override_ini:
|
||||||
for ini_config_list in self.option.override_ini:
|
for ini_config in ini_config_list:
|
||||||
for ini_config in ini_config_list:
|
try:
|
||||||
try:
|
(key, user_ini_value) = ini_config.split("=", 1)
|
||||||
(key, user_ini_value) = ini_config.split("=", 1)
|
except ValueError:
|
||||||
except ValueError:
|
raise UsageError("-o/--override-ini expects option=value style.")
|
||||||
raise UsageError("-o/--override-ini expects option=value style.")
|
if key == name:
|
||||||
if key == name:
|
value = user_ini_value
|
||||||
value = user_ini_value
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def getoption(self, name, default=notset, skip=False):
|
def getoption(self, name, default=notset, skip=False):
|
||||||
|
|
|
@ -246,7 +246,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):
|
def pytest_assertrepr_compare(config, op, left, right):
|
||||||
|
@ -255,7 +255,7 @@ def pytest_assertrepr_compare(config, op, left, right):
|
||||||
Return None for no custom explanation, otherwise return a list
|
Return None for no custom explanation, otherwise return a list
|
||||||
of strings. The strings will be joined by newlines but any newlines
|
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
|
*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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
@ -263,7 +263,14 @@ def pytest_assertrepr_compare(config, op, left, right):
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
def pytest_report_header(config, startdir):
|
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)
|
@hookspec(firstresult=True)
|
||||||
def pytest_report_teststatus(report):
|
def pytest_report_teststatus(report):
|
||||||
|
|
|
@ -119,7 +119,7 @@ class _NodeReporter(object):
|
||||||
node = kind(data, message=message)
|
node = kind(data, message=message)
|
||||||
self.append(node)
|
self.append(node)
|
||||||
|
|
||||||
def _write_captured_output(self, report):
|
def write_captured_output(self, report):
|
||||||
for capname in ('out', 'err'):
|
for capname in ('out', 'err'):
|
||||||
content = getattr(report, 'capstd' + capname)
|
content = getattr(report, 'capstd' + capname)
|
||||||
if content:
|
if content:
|
||||||
|
@ -128,7 +128,6 @@ class _NodeReporter(object):
|
||||||
|
|
||||||
def append_pass(self, report):
|
def append_pass(self, report):
|
||||||
self.add_stats('passed')
|
self.add_stats('passed')
|
||||||
self._write_captured_output(report)
|
|
||||||
|
|
||||||
def append_failure(self, report):
|
def append_failure(self, report):
|
||||||
# msg = str(report.longrepr.reprtraceback.extraline)
|
# msg = str(report.longrepr.reprtraceback.extraline)
|
||||||
|
@ -147,7 +146,6 @@ class _NodeReporter(object):
|
||||||
fail = Junit.failure(message=message)
|
fail = Junit.failure(message=message)
|
||||||
fail.append(bin_xml_escape(report.longrepr))
|
fail.append(bin_xml_escape(report.longrepr))
|
||||||
self.append(fail)
|
self.append(fail)
|
||||||
self._write_captured_output(report)
|
|
||||||
|
|
||||||
def append_collect_error(self, report):
|
def append_collect_error(self, report):
|
||||||
# msg = str(report.longrepr.reprtraceback.extraline)
|
# msg = str(report.longrepr.reprtraceback.extraline)
|
||||||
|
@ -165,7 +163,6 @@ class _NodeReporter(object):
|
||||||
msg = "test setup failure"
|
msg = "test setup failure"
|
||||||
self._add_simple(
|
self._add_simple(
|
||||||
Junit.error, msg, report.longrepr)
|
Junit.error, msg, report.longrepr)
|
||||||
self._write_captured_output(report)
|
|
||||||
|
|
||||||
def append_skipped(self, report):
|
def append_skipped(self, report):
|
||||||
if hasattr(report, "wasxfail"):
|
if hasattr(report, "wasxfail"):
|
||||||
|
@ -180,7 +177,7 @@ class _NodeReporter(object):
|
||||||
Junit.skipped("%s:%s: %s" % (filename, lineno, skipreason),
|
Junit.skipped("%s:%s: %s" % (filename, lineno, skipreason),
|
||||||
type="pytest.skip",
|
type="pytest.skip",
|
||||||
message=skipreason))
|
message=skipreason))
|
||||||
self._write_captured_output(report)
|
self.write_captured_output(report)
|
||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
data = self.to_xml().unicode(indent=0)
|
data = self.to_xml().unicode(indent=0)
|
||||||
|
@ -345,6 +342,8 @@ class LogXML(object):
|
||||||
reporter.append_skipped(report)
|
reporter.append_skipped(report)
|
||||||
self.update_testcase_duration(report)
|
self.update_testcase_duration(report)
|
||||||
if report.when == "teardown":
|
if report.when == "teardown":
|
||||||
|
reporter = self._opentestcase(report)
|
||||||
|
reporter.write_captured_output(report)
|
||||||
self.finalize(report)
|
self.finalize(report)
|
||||||
|
|
||||||
def update_testcase_duration(self, report):
|
def update_testcase_duration(self, report):
|
||||||
|
|
|
@ -81,7 +81,7 @@ def pytest_namespace():
|
||||||
|
|
||||||
|
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
pytest.config = config # compatibiltiy
|
pytest.config = config # compatibility
|
||||||
|
|
||||||
|
|
||||||
def wrap_session(config, doit):
|
def wrap_session(config, doit):
|
||||||
|
|
|
@ -66,7 +66,7 @@ def pytest_collection_modifyitems(items, config):
|
||||||
return
|
return
|
||||||
# pytest used to allow "-" for negating
|
# pytest used to allow "-" for negating
|
||||||
# but today we just allow "-" at the beginning, use "not" instead
|
# but today we just allow "-" at the beginning, use "not" instead
|
||||||
# we probably remove "-" alltogether soon
|
# we probably remove "-" altogether soon
|
||||||
if keywordexpr.startswith("-"):
|
if keywordexpr.startswith("-"):
|
||||||
keywordexpr = "not " + keywordexpr[1:]
|
keywordexpr = "not " + keywordexpr[1:]
|
||||||
selectuntil = False
|
selectuntil = False
|
||||||
|
|
|
@ -332,7 +332,7 @@ def testdir(request, tmpdir_factory):
|
||||||
return Testdir(request, tmpdir_factory)
|
return Testdir(request, tmpdir_factory)
|
||||||
|
|
||||||
|
|
||||||
rex_outcome = re.compile("(\d+) ([\w-]+)")
|
rex_outcome = re.compile(r"(\d+) ([\w-]+)")
|
||||||
class RunResult:
|
class RunResult:
|
||||||
"""The result of running a command.
|
"""The result of running a command.
|
||||||
|
|
||||||
|
@ -566,7 +566,7 @@ class Testdir:
|
||||||
def mkpydir(self, name):
|
def mkpydir(self, name):
|
||||||
"""Create a new python package.
|
"""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.
|
file so that is recognised as a python package.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -661,7 +661,7 @@ class Testdir:
|
||||||
def inline_genitems(self, *args):
|
def inline_genitems(self, *args):
|
||||||
"""Run ``pytest.main(['--collectonly'])`` in-process.
|
"""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.
|
:py:class:`HookRecorder` instance.
|
||||||
|
|
||||||
This runs the :py:func:`pytest.main` function to run all of
|
This runs the :py:func:`pytest.main` function to run all of
|
||||||
|
@ -857,7 +857,7 @@ class Testdir:
|
||||||
:py:meth:`parseconfigure`.
|
:py:meth:`parseconfigure`.
|
||||||
|
|
||||||
:param withinit: Whether to also write a ``__init__.py`` file
|
: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()}
|
kw = {self.request.function.__name__: Source(source).strip()}
|
||||||
|
|
|
@ -629,7 +629,7 @@ class Generator(FunctionMixin, PyCollector):
|
||||||
def getcallargs(self, obj):
|
def getcallargs(self, obj):
|
||||||
if not isinstance(obj, (tuple, list)):
|
if not isinstance(obj, (tuple, list)):
|
||||||
obj = (obj,)
|
obj = (obj,)
|
||||||
# explict naming
|
# explicit naming
|
||||||
if isinstance(obj[0], py.builtin._basestring):
|
if isinstance(obj[0], py.builtin._basestring):
|
||||||
name = obj[0]
|
name = obj[0]
|
||||||
obj = obj[1:]
|
obj = obj[1:]
|
||||||
|
|
|
@ -116,7 +116,7 @@ def tmpdir(request, tmpdir_factory):
|
||||||
path object.
|
path object.
|
||||||
"""
|
"""
|
||||||
name = request.node.name
|
name = request.node.name
|
||||||
name = re.sub("[\W]", "_", name)
|
name = re.sub(r"[\W]", "_", name)
|
||||||
MAXVAL = 30
|
MAXVAL = 30
|
||||||
if len(name) > MAXVAL:
|
if len(name) > MAXVAL:
|
||||||
name = name[:MAXVAL]
|
name = name[:MAXVAL]
|
||||||
|
|
|
@ -5,7 +5,7 @@ import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
# for transfering markers
|
# for transferring markers
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
from _pytest.python import transfer_markers
|
from _pytest.python import transfer_markers
|
||||||
from _pytest.skipping import MarkEvaluator
|
from _pytest.skipping import MarkEvaluator
|
||||||
|
|
|
@ -243,7 +243,9 @@ Fixture finalization / executing teardown code
|
||||||
|
|
||||||
pytest supports execution of fixture specific finalization 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
|
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
|
# 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
|
module itself does not need to change or know about these details
|
||||||
of fixture setup.
|
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
|
# content of test_yield2.py
|
||||||
|
|
||||||
|
import smtplib
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture(scope="module")
|
||||||
def passwd():
|
def smtp(request):
|
||||||
with open("/etc/passwd") as f:
|
with smtplib.SMTP("smtp.gmail.com") as smtp:
|
||||||
yield f.readlines()
|
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
|
The ``smtp`` connection will be closed after the test finished execution
|
||||||
because the Python ``file`` object supports finalization when
|
because the ``smtp`` object automatically closes when
|
||||||
the ``with`` statement ends.
|
the ``with`` statement ends.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -236,20 +236,31 @@ import ``helper.py`` normally. The contents of
|
||||||
Requiring/Loading plugins in a test module or conftest file
|
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
|
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"
|
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
|
This mechanism makes it easy to share fixtures within applications or even
|
||||||
assertion rewriting using the :func:`pytest.register_assert_rewrite`
|
external applications without the need to create external plugins using
|
||||||
mechanism. However for this to have any effect the module must not be
|
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
|
imported already; if it was already imported at the time the
|
||||||
``pytest_plugins`` statement is processed, a warning will result and
|
``pytest_plugins`` statement is processed, a warning will result and
|
||||||
assertions inside the plugin will not be re-written. To fix this you
|
assertions inside the plugin will not be re-written. To fix this you
|
||||||
|
|
|
@ -364,7 +364,7 @@ class TestAssert_reprcompare:
|
||||||
expl = '\n'.join(callequal(left, right, verbose=True))
|
expl = '\n'.join(callequal(left, right, verbose=True))
|
||||||
assert expl.endswith(textwrap.dedent(expected).strip())
|
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])
|
expl = callequal([0, 1], [0, 1, 2])
|
||||||
assert len(expl) > 1
|
assert len(expl) > 1
|
||||||
expl = callequal([0, 1, 2], [0, 1])
|
expl = callequal([0, 1, 2], [0, 1])
|
||||||
|
|
|
@ -271,7 +271,7 @@ class TestAssertionRewrite:
|
||||||
|
|
||||||
getmsg(f, must_pass=True)
|
getmsg(f, must_pass=True)
|
||||||
|
|
||||||
def test_short_circut_evaluation(self):
|
def test_short_circuit_evaluation(self):
|
||||||
def f():
|
def f():
|
||||||
assert True or explode # noqa
|
assert True or explode # noqa
|
||||||
|
|
||||||
|
|
|
@ -604,7 +604,7 @@ def test_capture_binary_output(testdir):
|
||||||
|
|
||||||
|
|
||||||
def test_error_during_readouterr(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="""
|
testdir.makepyfile(pytest_xyz="""
|
||||||
from _pytest.capture import FDCapture
|
from _pytest.capture import FDCapture
|
||||||
def bad_snap(self):
|
def bad_snap(self):
|
||||||
|
|
|
@ -778,6 +778,21 @@ class TestOverrideIniArgs:
|
||||||
result = testdir.runpytest("--override-ini", 'xdist_strict True', "-s")
|
result = testdir.runpytest("--override-ini", 'xdist_strict True', "-s")
|
||||||
result.stderr.fnmatch_lines(["*ERROR* *expects option=value*"])
|
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):
|
def test_with_arg_outside_cwd_without_inifile(self, tmpdir, monkeypatch):
|
||||||
monkeypatch.chdir(str(tmpdir))
|
monkeypatch.chdir(str(tmpdir))
|
||||||
a = tmpdir.mkdir("a")
|
a = tmpdir.mkdir("a")
|
||||||
|
|
|
@ -557,6 +557,25 @@ class TestPython:
|
||||||
systemout = pnode.find_first_by_tag("system-err")
|
systemout = pnode.find_first_by_tag("system-err")
|
||||||
assert "hello-stderr" in systemout.toxml()
|
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():
|
def test_mangle_test_address():
|
||||||
from _pytest.junitxml import mangle_test_address
|
from _pytest.junitxml import mangle_test_address
|
||||||
|
|
|
@ -197,7 +197,7 @@ class TestNewSession(SessionTests):
|
||||||
colfail = [x for x in finished if x.failed]
|
colfail = [x for x in finished if x.failed]
|
||||||
assert len(colfail) == 1
|
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(__init__="")
|
||||||
testdir.makepyfile(test_one="xxxx", test_two="yyyy", test_third="zzz")
|
testdir.makepyfile(test_one="xxxx", test_two="yyyy", test_third="zzz")
|
||||||
reprec = testdir.inline_run("-x", "--maxfail=2", testdir.tmpdir)
|
reprec = testdir.inline_run("-x", "--maxfail=2", testdir.tmpdir)
|
||||||
|
|
Loading…
Reference in New Issue