Merge pull request #2869 from nicoddemus/merge-master-into-features
Merge master into features
This commit is contained in:
commit
def471b975
|
@ -12,4 +12,4 @@ Here's a quick checklist that should be present in PRs:
|
||||||
|
|
||||||
Unless your change is a trivial or a documentation fix (e.g., a typo or reword of a small section) please:
|
Unless your change is a trivial or a documentation fix (e.g., a typo or reword of a small section) please:
|
||||||
|
|
||||||
- [ ] Add yourself to `AUTHORS`;
|
- [ ] Add yourself to `AUTHORS`, in alphabetical order;
|
||||||
|
|
1
AUTHORS
1
AUTHORS
|
@ -171,6 +171,7 @@ Tareq Alayan
|
||||||
Ted Xiao
|
Ted Xiao
|
||||||
Thomas Grainger
|
Thomas Grainger
|
||||||
Thomas Hisch
|
Thomas Hisch
|
||||||
|
Tom Dalton
|
||||||
Tom Viner
|
Tom Viner
|
||||||
Trevor Bekolay
|
Trevor Bekolay
|
||||||
Tyler Goodlet
|
Tyler Goodlet
|
||||||
|
|
|
@ -177,7 +177,8 @@ Short version
|
||||||
#. Write a ``changelog`` entry: ``changelog/2574.bugfix``, use issue id number
|
#. Write a ``changelog`` entry: ``changelog/2574.bugfix``, use issue id number
|
||||||
and one of ``bugfix``, ``removal``, ``feature``, ``vendor``, ``doc`` or
|
and one of ``bugfix``, ``removal``, ``feature``, ``vendor``, ``doc`` or
|
||||||
``trivial`` for the issue type.
|
``trivial`` for the issue type.
|
||||||
#. Add yourself to ``AUTHORS`` file if not there yet, in alphabetical order.
|
#. Unless your change is a trivial or a documentation fix (e.g., a typo or reword of a small section) please
|
||||||
|
add yourself to the ``AUTHORS`` file, in alphabetical order;
|
||||||
|
|
||||||
|
|
||||||
Long version
|
Long version
|
||||||
|
|
|
@ -14,7 +14,6 @@ import py
|
||||||
import _pytest
|
import _pytest
|
||||||
from _pytest.outcomes import TEST_OUTCOME
|
from _pytest.outcomes import TEST_OUTCOME
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import enum
|
import enum
|
||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
|
@ -110,10 +109,9 @@ def getfuncargnames(function, is_method=False, cls=None):
|
||||||
# ordered mapping of parameter names to Parameter instances. This
|
# ordered mapping of parameter names to Parameter instances. This
|
||||||
# creates a tuple of the names of the parameters that don't have
|
# creates a tuple of the names of the parameters that don't have
|
||||||
# defaults.
|
# defaults.
|
||||||
arg_names = tuple(
|
arg_names = tuple(p.name for p in signature(function).parameters.values()
|
||||||
p.name for p in signature(function).parameters.values()
|
if (p.kind is Parameter.POSITIONAL_OR_KEYWORD or
|
||||||
if (p.kind is Parameter.POSITIONAL_OR_KEYWORD
|
p.kind is Parameter.KEYWORD_ONLY) and
|
||||||
or p.kind is Parameter.KEYWORD_ONLY) and
|
|
||||||
p.default is Parameter.empty)
|
p.default is Parameter.empty)
|
||||||
# If this function should be treated as a bound method even though
|
# If this function should be treated as a bound method even though
|
||||||
# it's passed as an unbound method or function, remove the first
|
# it's passed as an unbound method or function, remove the first
|
||||||
|
@ -129,8 +127,6 @@ def getfuncargnames(function, is_method=False, cls=None):
|
||||||
|
|
||||||
|
|
||||||
if _PY3:
|
if _PY3:
|
||||||
imap = map
|
|
||||||
izip = zip
|
|
||||||
STRING_TYPES = bytes, str
|
STRING_TYPES = bytes, str
|
||||||
UNICODE_TYPES = str,
|
UNICODE_TYPES = str,
|
||||||
|
|
||||||
|
@ -173,8 +169,6 @@ else:
|
||||||
STRING_TYPES = bytes, str, unicode
|
STRING_TYPES = bytes, str, unicode
|
||||||
UNICODE_TYPES = unicode,
|
UNICODE_TYPES = unicode,
|
||||||
|
|
||||||
from itertools import imap, izip # NOQA
|
|
||||||
|
|
||||||
def ascii_escaped(val):
|
def ascii_escaped(val):
|
||||||
"""In py2 bytes and str are the same type, so return if it's a bytes
|
"""In py2 bytes and str are the same type, so return if it's a bytes
|
||||||
object, return it unchanged if it is a full ascii string,
|
object, return it unchanged if it is a full ascii string,
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
from __future__ import absolute_import, division, print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
import sys
|
|
||||||
|
|
||||||
from py._code.code import FormattedExcinfo
|
|
||||||
|
|
||||||
import py
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
|
import sys
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
import py
|
||||||
|
from py._code.code import FormattedExcinfo
|
||||||
|
|
||||||
import _pytest
|
import _pytest
|
||||||
|
from _pytest import nodes
|
||||||
from _pytest._code.code import TerminalRepr
|
from _pytest._code.code import TerminalRepr
|
||||||
from _pytest.compat import (
|
from _pytest.compat import (
|
||||||
NOTSET, exc_clear, _format_args,
|
NOTSET, exc_clear, _format_args,
|
||||||
|
@ -15,9 +16,10 @@ from _pytest.compat import (
|
||||||
is_generator, isclass, getimfunc,
|
is_generator, isclass, getimfunc,
|
||||||
getlocation, getfuncargnames,
|
getlocation, getfuncargnames,
|
||||||
safe_getattr,
|
safe_getattr,
|
||||||
|
FuncargnamesCompatAttr,
|
||||||
)
|
)
|
||||||
from _pytest.outcomes import fail, TEST_OUTCOME
|
from _pytest.outcomes import fail, TEST_OUTCOME
|
||||||
from _pytest.compat import FuncargnamesCompatAttr
|
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
@ -977,8 +979,8 @@ class FixtureManager:
|
||||||
# by their test id)
|
# by their test id)
|
||||||
if p.basename.startswith("conftest.py"):
|
if p.basename.startswith("conftest.py"):
|
||||||
nodeid = p.dirpath().relto(self.config.rootdir)
|
nodeid = p.dirpath().relto(self.config.rootdir)
|
||||||
if p.sep != "/":
|
if p.sep != nodes.SEP:
|
||||||
nodeid = nodeid.replace(p.sep, "/")
|
nodeid = nodeid.replace(p.sep, nodes.SEP)
|
||||||
self.parsefactories(plugin, nodeid)
|
self.parsefactories(plugin, nodeid)
|
||||||
|
|
||||||
def _getautousenames(self, nodeid):
|
def _getautousenames(self, nodeid):
|
||||||
|
@ -1033,8 +1035,13 @@ class FixtureManager:
|
||||||
if faclist:
|
if faclist:
|
||||||
fixturedef = faclist[-1]
|
fixturedef = faclist[-1]
|
||||||
if fixturedef.params is not None:
|
if fixturedef.params is not None:
|
||||||
func_params = getattr(getattr(metafunc.function, 'parametrize', None), 'args', [[None]])
|
parametrize_func = getattr(metafunc.function, 'parametrize', None)
|
||||||
|
func_params = getattr(parametrize_func, 'args', [[None]])
|
||||||
|
func_kwargs = getattr(parametrize_func, 'kwargs', {})
|
||||||
# skip directly parametrized arguments
|
# skip directly parametrized arguments
|
||||||
|
if "argnames" in func_kwargs:
|
||||||
|
argnames = parametrize_func.kwargs["argnames"]
|
||||||
|
else:
|
||||||
argnames = func_params[0]
|
argnames = func_params[0]
|
||||||
if not isinstance(argnames, (tuple, list)):
|
if not isinstance(argnames, (tuple, list)):
|
||||||
argnames = [x.strip() for x in argnames.split(",") if x.strip()]
|
argnames = [x.strip() for x in argnames.split(",") if x.strip()]
|
||||||
|
@ -1123,5 +1130,5 @@ class FixtureManager:
|
||||||
|
|
||||||
def _matchfactories(self, fixturedefs, nodeid):
|
def _matchfactories(self, fixturedefs, nodeid):
|
||||||
for fixturedef in fixturedefs:
|
for fixturedef in fixturedefs:
|
||||||
if nodeid.startswith(fixturedef.baseid):
|
if nodes.ischildnode(fixturedef.baseid, nodeid):
|
||||||
yield fixturedef
|
yield fixturedef
|
||||||
|
|
|
@ -17,6 +17,7 @@ import re
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import pytest
|
import pytest
|
||||||
|
from _pytest import nodes
|
||||||
from _pytest.config import filename_arg
|
from _pytest.config import filename_arg
|
||||||
|
|
||||||
# Python 2.X and 3.X compatibility
|
# Python 2.X and 3.X compatibility
|
||||||
|
@ -252,7 +253,7 @@ def mangle_test_address(address):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
# convert file path to dotted path
|
# convert file path to dotted path
|
||||||
names[0] = names[0].replace("/", '.')
|
names[0] = names[0].replace(nodes.SEP, '.')
|
||||||
names[0] = _py_ext_re.sub("", names[0])
|
names[0] = _py_ext_re.sub("", names[0])
|
||||||
# put any params back
|
# put any params back
|
||||||
names[-1] += possible_open_bracket + params
|
names[-1] += possible_open_bracket + params
|
||||||
|
|
|
@ -7,6 +7,7 @@ import six
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import _pytest
|
import _pytest
|
||||||
|
from _pytest import nodes
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
import py
|
import py
|
||||||
try:
|
try:
|
||||||
|
@ -15,8 +16,8 @@ except ImportError:
|
||||||
from UserDict import DictMixin as MappingMixin
|
from UserDict import DictMixin as MappingMixin
|
||||||
|
|
||||||
from _pytest.config import directory_arg, UsageError, hookimpl
|
from _pytest.config import directory_arg, UsageError, hookimpl
|
||||||
from _pytest.runner import collect_one_node
|
|
||||||
from _pytest.outcomes import exit
|
from _pytest.outcomes import exit
|
||||||
|
from _pytest.runner import collect_one_node
|
||||||
|
|
||||||
tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
|
tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
|
||||||
|
|
||||||
|
@ -494,14 +495,14 @@ class FSCollector(Collector):
|
||||||
rel = fspath.relto(parent.fspath)
|
rel = fspath.relto(parent.fspath)
|
||||||
if rel:
|
if rel:
|
||||||
name = rel
|
name = rel
|
||||||
name = name.replace(os.sep, "/")
|
name = name.replace(os.sep, nodes.SEP)
|
||||||
super(FSCollector, self).__init__(name, parent, config, session)
|
super(FSCollector, self).__init__(name, parent, config, session)
|
||||||
self.fspath = fspath
|
self.fspath = fspath
|
||||||
|
|
||||||
def _makeid(self):
|
def _makeid(self):
|
||||||
relpath = self.fspath.relto(self.config.rootdir)
|
relpath = self.fspath.relto(self.config.rootdir)
|
||||||
if os.sep != "/":
|
if os.sep != nodes.SEP:
|
||||||
relpath = relpath.replace(os.sep, "/")
|
relpath = relpath.replace(os.sep, nodes.SEP)
|
||||||
return relpath
|
return relpath
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import inspect
|
||||||
import warnings
|
import warnings
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from .compat import imap
|
from six.moves import map
|
||||||
from .deprecated import MARK_PARAMETERSET_UNPACKING
|
from .deprecated import MARK_PARAMETERSET_UNPACKING
|
||||||
|
|
||||||
|
|
||||||
|
@ -379,7 +379,7 @@ def store_mark(obj, mark):
|
||||||
"""
|
"""
|
||||||
assert isinstance(mark, Mark), mark
|
assert isinstance(mark, Mark), mark
|
||||||
# always reassign name to avoid updating pytestmark
|
# always reassign name to avoid updating pytestmark
|
||||||
# in a referene that was only borrowed
|
# in a reference that was only borrowed
|
||||||
obj.pytestmark = get_unpacked_marks(obj) + [mark]
|
obj.pytestmark = get_unpacked_marks(obj) + [mark]
|
||||||
|
|
||||||
|
|
||||||
|
@ -427,7 +427,7 @@ class MarkInfo(object):
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
""" yield MarkInfo objects each relating to a marking-call. """
|
""" yield MarkInfo objects each relating to a marking-call. """
|
||||||
return imap(MarkInfo, self._marks)
|
return map(MarkInfo, self._marks)
|
||||||
|
|
||||||
|
|
||||||
MARK_GEN = MarkGenerator()
|
MARK_GEN = MarkGenerator()
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
SEP = "/"
|
||||||
|
|
||||||
|
|
||||||
|
def _splitnode(nodeid):
|
||||||
|
"""Split a nodeid into constituent 'parts'.
|
||||||
|
|
||||||
|
Node IDs are strings, and can be things like:
|
||||||
|
''
|
||||||
|
'testing/code'
|
||||||
|
'testing/code/test_excinfo.py'
|
||||||
|
'testing/code/test_excinfo.py::TestFormattedExcinfo::()'
|
||||||
|
|
||||||
|
Return values are lists e.g.
|
||||||
|
[]
|
||||||
|
['testing', 'code']
|
||||||
|
['testing', 'code', 'test_excinfo.py']
|
||||||
|
['testing', 'code', 'test_excinfo.py', 'TestFormattedExcinfo', '()']
|
||||||
|
"""
|
||||||
|
if nodeid == '':
|
||||||
|
# If there is no root node at all, return an empty list so the caller's logic can remain sane
|
||||||
|
return []
|
||||||
|
parts = nodeid.split(SEP)
|
||||||
|
# Replace single last element 'test_foo.py::Bar::()' with multiple elements 'test_foo.py', 'Bar', '()'
|
||||||
|
parts[-1:] = parts[-1].split("::")
|
||||||
|
return parts
|
||||||
|
|
||||||
|
|
||||||
|
def ischildnode(baseid, nodeid):
|
||||||
|
"""Return True if the nodeid is a child node of the baseid.
|
||||||
|
|
||||||
|
E.g. 'foo/bar::Baz::()' is a child of 'foo', 'foo/bar' and 'foo/bar::Baz', but not of 'foo/blorp'
|
||||||
|
"""
|
||||||
|
base_parts = _splitnode(baseid)
|
||||||
|
node_parts = _splitnode(nodeid)
|
||||||
|
if len(node_parts) < len(base_parts):
|
||||||
|
return False
|
||||||
|
return node_parts[:len(base_parts)] == base_parts
|
|
@ -23,9 +23,7 @@ from _pytest.main import Session, EXIT_OK
|
||||||
from _pytest.assertion.rewrite import AssertionRewritingHook
|
from _pytest.assertion.rewrite import AssertionRewritingHook
|
||||||
|
|
||||||
|
|
||||||
PYTEST_FULLPATH = os.path.abspath(
|
PYTEST_FULLPATH = os.path.abspath(pytest.__file__.rstrip("oc")).replace("$py.class", ".py")
|
||||||
pytest.__file__.rstrip("oc")
|
|
||||||
).replace("$py.class", ".py")
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
|
|
|
@ -2,8 +2,9 @@ import math
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
from six.moves import zip
|
||||||
|
|
||||||
from _pytest.compat import isclass, izip
|
from _pytest.compat import isclass
|
||||||
from _pytest.outcomes import fail
|
from _pytest.outcomes import fail
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
|
|
||||||
|
@ -145,7 +146,7 @@ class ApproxSequence(ApproxBase):
|
||||||
return ApproxBase.__eq__(self, actual)
|
return ApproxBase.__eq__(self, actual)
|
||||||
|
|
||||||
def _yield_comparisons(self, actual):
|
def _yield_comparisons(self, actual):
|
||||||
return izip(actual, self.expected)
|
return zip(actual, self.expected)
|
||||||
|
|
||||||
|
|
||||||
class ApproxScalar(ApproxBase):
|
class ApproxScalar(ApproxBase):
|
||||||
|
@ -217,7 +218,8 @@ class ApproxScalar(ApproxBase):
|
||||||
absolute tolerance or a relative tolerance, depending on what the user
|
absolute tolerance or a relative tolerance, depending on what the user
|
||||||
specified or which would be larger.
|
specified or which would be larger.
|
||||||
"""
|
"""
|
||||||
def set_default(x, default): return x if x is not None else default
|
def set_default(x, default):
|
||||||
|
return x if x is not None else default
|
||||||
|
|
||||||
# Figure out what the absolute tolerance should be. ``self.abs`` is
|
# Figure out what the absolute tolerance should be. ``self.abs`` is
|
||||||
# either None or a value specified by the user.
|
# either None or a value specified by the user.
|
||||||
|
|
|
@ -232,7 +232,5 @@ class WarningsChecker(WarningsRecorder):
|
||||||
else:
|
else:
|
||||||
fail("DID NOT WARN. No warnings of type {0} matching"
|
fail("DID NOT WARN. No warnings of type {0} matching"
|
||||||
" ('{1}') was emitted. The list of emitted warnings"
|
" ('{1}') was emitted. The list of emitted warnings"
|
||||||
" is: {2}.".format(
|
" is: {2}.".format(self.expected_warning, self.match_expr,
|
||||||
self.expected_warning,
|
|
||||||
self.match_expr,
|
|
||||||
[each.message for each in self]))
|
[each.message for each in self]))
|
||||||
|
|
|
@ -10,11 +10,12 @@ import sys
|
||||||
import time
|
import time
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
import pluggy
|
||||||
import py
|
import py
|
||||||
import six
|
import six
|
||||||
|
|
||||||
import pluggy
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from _pytest import nodes
|
||||||
from _pytest.main import EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, \
|
from _pytest.main import EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, \
|
||||||
EXIT_USAGEERROR, EXIT_NOTESTSCOLLECTED
|
EXIT_USAGEERROR, EXIT_NOTESTSCOLLECTED
|
||||||
|
|
||||||
|
@ -466,7 +467,7 @@ class TerminalReporter:
|
||||||
|
|
||||||
if fspath:
|
if fspath:
|
||||||
res = mkrel(nodeid).replace("::()", "") # parens-normalization
|
res = mkrel(nodeid).replace("::()", "") # parens-normalization
|
||||||
if nodeid.split("::")[0] != fspath.replace("\\", "/"):
|
if nodeid.split("::")[0] != fspath.replace("\\", nodes.SEP):
|
||||||
res += " <- " + self.startdir.bestrelpath(fspath)
|
res += " <- " + self.startdir.bestrelpath(fspath)
|
||||||
else:
|
else:
|
||||||
res = "[location]"
|
res = "[location]"
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Explicitly mention ``xpass`` in the documentation of ``xfail``.
|
|
@ -0,0 +1 @@
|
||||||
|
Fix issue with @pytest.parametrize if argnames was specified as kwarg.
|
|
@ -0,0 +1 @@
|
||||||
|
Match fixture paths against actual path segments in order to avoid matching folders which share a prefix.
|
|
@ -0,0 +1 @@
|
||||||
|
Clarify the documentation of available fixture scopes.
|
|
@ -0,0 +1 @@
|
||||||
|
Add documentation about the ``python -m pytest`` invocation adding the current directory to sys.path.
|
|
@ -350,7 +350,7 @@ Parametrizing test methods through per-class configuration
|
||||||
.. _`unittest parametrizer`: https://github.com/testing-cabal/unittest-ext/blob/master/params.py
|
.. _`unittest parametrizer`: https://github.com/testing-cabal/unittest-ext/blob/master/params.py
|
||||||
|
|
||||||
|
|
||||||
Here is an example ``pytest_generate_function`` function implementing a
|
Here is an example ``pytest_generate_tests`` function implementing a
|
||||||
parametrization scheme similar to Michael Foord's `unittest
|
parametrization scheme similar to Michael Foord's `unittest
|
||||||
parametrizer`_ but in a lot less code::
|
parametrizer`_ but in a lot less code::
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ functions:
|
||||||
* fixture management scales from simple unit to complex
|
* fixture management scales from simple unit to complex
|
||||||
functional testing, allowing to parametrize fixtures and tests according
|
functional testing, allowing to parametrize fixtures and tests according
|
||||||
to configuration and component options, or to re-use fixtures
|
to configuration and component options, or to re-use fixtures
|
||||||
across class, module or whole test session scopes.
|
across function, class, module or whole test session scopes.
|
||||||
|
|
||||||
In addition, pytest continues to support :ref:`xunitsetup`. You can mix
|
In addition, pytest continues to support :ref:`xunitsetup`. You can mix
|
||||||
both styles, moving incrementally from classic to new style, as you
|
both styles, moving incrementally from classic to new style, as you
|
||||||
|
@ -129,8 +129,8 @@ functions take the role of the *injector* and test functions are the
|
||||||
|
|
||||||
.. _smtpshared:
|
.. _smtpshared:
|
||||||
|
|
||||||
Sharing a fixture across tests in a module (or class/session)
|
Scope: Sharing a fixture across tests in a class, module or session
|
||||||
-----------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
|
|
||||||
.. regendoc:wipe
|
.. regendoc:wipe
|
||||||
|
|
||||||
|
@ -139,10 +139,12 @@ usually time-expensive to create. Extending the previous example, we
|
||||||
can add a ``scope='module'`` parameter to the
|
can add a ``scope='module'`` parameter to the
|
||||||
:py:func:`@pytest.fixture <_pytest.python.fixture>` invocation
|
:py:func:`@pytest.fixture <_pytest.python.fixture>` invocation
|
||||||
to cause the decorated ``smtp`` fixture function to only be invoked once
|
to cause the decorated ``smtp`` fixture function to only be invoked once
|
||||||
per test module. Multiple test functions in a test module will thus
|
per test *module* (the default is to invoke once per test *function*).
|
||||||
each receive the same ``smtp`` fixture instance. The next example puts
|
Multiple test functions in a test module will thus
|
||||||
the fixture function into a separate ``conftest.py`` file so
|
each receive the same ``smtp`` fixture instance, thus saving time.
|
||||||
that tests from multiple test modules in the directory can
|
|
||||||
|
The next example puts the fixture function into a separate ``conftest.py`` file
|
||||||
|
so that tests from multiple test modules in the directory can
|
||||||
access the fixture function::
|
access the fixture function::
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
|
@ -223,6 +225,8 @@ instance, you can simply declare it:
|
||||||
# the returned fixture value will be shared for
|
# the returned fixture value will be shared for
|
||||||
# all tests needing it
|
# all tests needing it
|
||||||
|
|
||||||
|
Finally, the ``class`` scope will invoke the fixture once per test *class*.
|
||||||
|
|
||||||
.. _`finalization`:
|
.. _`finalization`:
|
||||||
|
|
||||||
Fixture finalization / executing teardown code
|
Fixture finalization / executing teardown code
|
||||||
|
@ -858,7 +862,7 @@ into a conftest.py file **without** using ``autouse``::
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def transact(self, request, db):
|
def transact(request, db):
|
||||||
db.begin()
|
db.begin()
|
||||||
yield
|
yield
|
||||||
db.rollback()
|
db.rollback()
|
||||||
|
|
|
@ -68,4 +68,9 @@ imported in the global import namespace.
|
||||||
|
|
||||||
This is also discussed in details in :ref:`test discovery`.
|
This is also discussed in details in :ref:`test discovery`.
|
||||||
|
|
||||||
|
Invoking ``pytest`` versus ``python -m pytest``
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
Running pytest with ``python -m pytest [...]`` instead of ``pytest [...]`` yields nearly
|
||||||
|
equivalent behaviour, except that the former call will add the current directory to ``sys.path``.
|
||||||
|
See also :ref:`cmdline`.
|
||||||
|
|
|
@ -16,13 +16,17 @@ resource which is not available at the moment (for example a database).
|
||||||
|
|
||||||
A **xfail** means that you expect a test to fail for some reason.
|
A **xfail** means that you expect a test to fail for some reason.
|
||||||
A common example is a test for a feature not yet implemented, or a bug not yet fixed.
|
A common example is a test for a feature not yet implemented, or a bug not yet fixed.
|
||||||
|
When a test passes despite being expected to fail (marked with ``pytest.mark.xfail``),
|
||||||
|
it's an **xpass** and will be reported in the test summary.
|
||||||
|
|
||||||
``pytest`` counts and lists *skip* and *xfail* tests separately. Detailed
|
``pytest`` counts and lists *skip* and *xfail* tests separately. Detailed
|
||||||
information about skipped/xfailed tests is not shown by default to avoid
|
information about skipped/xfailed tests is not shown by default to avoid
|
||||||
cluttering the output. You can use the ``-r`` option to see details
|
cluttering the output. You can use the ``-r`` option to see details
|
||||||
corresponding to the "short" letters shown in the test progress::
|
corresponding to the "short" letters shown in the test progress::
|
||||||
|
|
||||||
pytest -rxs # show extra info on skips and xfails
|
pytest -rxXs # show extra info on xfailed, xpassed, and skipped tests
|
||||||
|
|
||||||
|
More details on the ``-r`` option can be found by running ``pytest -h``.
|
||||||
|
|
||||||
(See :ref:`how to change command line options defaults`)
|
(See :ref:`how to change command line options defaults`)
|
||||||
|
|
||||||
|
|
|
@ -233,3 +233,13 @@ was executed ahead of the ``test_method``.
|
||||||
overwrite ``unittest.TestCase`` ``__call__`` or ``run``, they need to
|
overwrite ``unittest.TestCase`` ``__call__`` or ``run``, they need to
|
||||||
to overwrite ``debug`` in the same way (this is also true for standard
|
to overwrite ``debug`` in the same way (this is also true for standard
|
||||||
unittest).
|
unittest).
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Due to architectural differences between the two frameworks, setup and
|
||||||
|
teardown for ``unittest``-based tests is performed during the ``call`` phase
|
||||||
|
of testing instead of in ``pytest``'s standard ``setup`` and ``teardown``
|
||||||
|
stages. This can be important to understand in some situations, particularly
|
||||||
|
when reasoning about errors. For example, if a ``unittest``-based suite
|
||||||
|
exhibits errors during setup, ``pytest`` will report no errors during its
|
||||||
|
``setup`` phase and will instead raise the error during ``call``.
|
||||||
|
|
|
@ -17,7 +17,7 @@ You can invoke testing through the Python interpreter from the command line::
|
||||||
python -m pytest [...]
|
python -m pytest [...]
|
||||||
|
|
||||||
This is almost equivalent to invoking the command line script ``pytest [...]``
|
This is almost equivalent to invoking the command line script ``pytest [...]``
|
||||||
directly, except that Python will also add the current directory to ``sys.path``.
|
directly, except that calling via ``python`` will also add the current directory to ``sys.path``.
|
||||||
|
|
||||||
Possible exit codes
|
Possible exit codes
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
|
@ -109,7 +109,7 @@ decorator or to all tests in a module by setting the ``pytestmark`` variable:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# turns all warnings into errors for this module
|
# turns all warnings into errors for this module
|
||||||
pytestmark = @pytest.mark.filterwarnings('error')
|
pytestmark = pytest.mark.filterwarnings('error')
|
||||||
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import absolute_import, division, print_function
|
||||||
import pytest
|
import pytest
|
||||||
import py
|
import py
|
||||||
|
|
||||||
|
import _pytest._code
|
||||||
from _pytest.main import Session, EXIT_NOTESTSCOLLECTED, _in_venv
|
from _pytest.main import Session, EXIT_NOTESTSCOLLECTED, _in_venv
|
||||||
|
|
||||||
|
|
||||||
|
@ -829,3 +830,28 @@ def test_continue_on_collection_errors_maxfail(testdir):
|
||||||
"collected 2 items / 2 errors",
|
"collected 2 items / 2 errors",
|
||||||
"*1 failed, 2 error*",
|
"*1 failed, 2 error*",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def test_fixture_scope_sibling_conftests(testdir):
|
||||||
|
"""Regression test case for https://github.com/pytest-dev/pytest/issues/2836"""
|
||||||
|
foo_path = testdir.mkpydir("foo")
|
||||||
|
foo_path.join("conftest.py").write(_pytest._code.Source("""
|
||||||
|
import pytest
|
||||||
|
@pytest.fixture
|
||||||
|
def fix():
|
||||||
|
return 1
|
||||||
|
"""))
|
||||||
|
foo_path.join("test_foo.py").write("def test_foo(fix): assert fix == 1")
|
||||||
|
|
||||||
|
# Tests in `food/` should not see the conftest fixture from `foo/`
|
||||||
|
food_path = testdir.mkpydir("food")
|
||||||
|
food_path.join("test_food.py").write("def test_food(fix): assert fix == 1")
|
||||||
|
|
||||||
|
res = testdir.runpytest()
|
||||||
|
assert res.ret == 1
|
||||||
|
|
||||||
|
res.stdout.fnmatch_lines([
|
||||||
|
"*ERROR at setup of test_food*",
|
||||||
|
"E*fixture 'fix' not found",
|
||||||
|
"*1 passed, 1 error*",
|
||||||
|
])
|
||||||
|
|
|
@ -342,6 +342,24 @@ def test_parametrized_collect_with_wrong_args(testdir):
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def test_parametrized_with_kwargs(testdir):
|
||||||
|
"""Test collect parametrized func with wrong number of args."""
|
||||||
|
py_file = testdir.makepyfile("""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
@pytest.fixture(params=[1,2])
|
||||||
|
def a(request):
|
||||||
|
return request.param
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(argnames='b', argvalues=[1, 2])
|
||||||
|
def test_func(a, b):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
|
||||||
|
result = testdir.runpytest(py_file)
|
||||||
|
assert(result.ret == 0)
|
||||||
|
|
||||||
|
|
||||||
class TestFunctional(object):
|
class TestFunctional(object):
|
||||||
|
|
||||||
def test_mark_per_function(self, testdir):
|
def test_mark_per_function(self, testdir):
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from _pytest import nodes
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("baseid, nodeid, expected", (
|
||||||
|
('', '', True),
|
||||||
|
('', 'foo', True),
|
||||||
|
('', 'foo/bar', True),
|
||||||
|
('', 'foo/bar::TestBaz::()', True),
|
||||||
|
('foo', 'food', False),
|
||||||
|
('foo/bar::TestBaz::()', 'foo/bar', False),
|
||||||
|
('foo/bar::TestBaz::()', 'foo/bar::TestBop::()', False),
|
||||||
|
('foo/bar', 'foo/bar::TestBop::()', True),
|
||||||
|
))
|
||||||
|
def test_ischildnode(baseid, nodeid, expected):
|
||||||
|
result = nodes.ischildnode(baseid, nodeid)
|
||||||
|
assert result is expected
|
Loading…
Reference in New Issue