Merge branch 'master' into fix-missing-nodeid-with-pyargs

This commit is contained in:
Ronny Pfannschmidt 2017-11-11 14:33:55 +01:00 committed by GitHub
commit f0f2d2b861
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
85 changed files with 1118 additions and 616 deletions

View File

@ -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;

View File

@ -46,6 +46,7 @@ Dave Hunt
David Díaz-Barquero David Díaz-Barquero
David Mohr David Mohr
David Vierra David Vierra
Daw-Ran Liou
Denis Kirisov Denis Kirisov
Diego Russo Diego Russo
Dmitry Dygalo Dmitry Dygalo
@ -164,6 +165,7 @@ Stephan Obermann
Tareq Alayan Tareq Alayan
Ted Xiao Ted Xiao
Thomas Grainger Thomas Grainger
Tom Dalton
Tom Viner Tom Viner
Trevor Bekolay Trevor Bekolay
Tyler Goodlet Tyler Goodlet

View File

@ -8,6 +8,46 @@
.. towncrier release notes start .. towncrier release notes start
Pytest 3.2.3 (2017-10-03)
=========================
Bug Fixes
---------
- Fix crash in tab completion when no prefix is given. (`#2748
<https://github.com/pytest-dev/pytest/issues/2748>`_)
- The equality checking function (``__eq__``) of ``MarkDecorator`` returns
``False`` if one object is not an instance of ``MarkDecorator``. (`#2758
<https://github.com/pytest-dev/pytest/issues/2758>`_)
- When running ``pytest --fixtures-per-test``: don't crash if an item has no
_fixtureinfo attribute (e.g. doctests) (`#2788
<https://github.com/pytest-dev/pytest/issues/2788>`_)
Improved Documentation
----------------------
- In help text of ``-k`` option, add example of using ``not`` to not select
certain tests whose names match the provided expression. (`#1442
<https://github.com/pytest-dev/pytest/issues/1442>`_)
- Add note in ``parametrize.rst`` about calling ``metafunc.parametrize``
multiple times. (`#1548 <https://github.com/pytest-dev/pytest/issues/1548>`_)
Trivial/Internal Changes
------------------------
- Set ``xfail_strict=True`` in pytest's own test suite to catch expected
failures as soon as they start to pass. (`#2722
<https://github.com/pytest-dev/pytest/issues/2722>`_)
- Fix typo in example of passing a callable to markers (in example/markers.rst)
(`#2765 <https://github.com/pytest-dev/pytest/issues/2765>`_)
Pytest 3.2.2 (2017-09-06) Pytest 3.2.2 (2017-09-06)
========================= =========================

View File

@ -120,7 +120,7 @@ the following:
- PyPI presence with a ``setup.py`` that contains a license, ``pytest-`` - PyPI presence with a ``setup.py`` that contains a license, ``pytest-``
prefixed name, version number, authors, short and long description. prefixed name, version number, authors, short and long description.
- a ``tox.ini`` for running tests using `tox <http://tox.testrun.org>`_. - a ``tox.ini`` for running tests using `tox <https://tox.readthedocs.io>`_.
- a ``README.txt`` describing how to use the plugin and on which - a ``README.txt`` describing how to use the plugin and on which
platforms it runs. platforms it runs.
@ -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

View File

@ -1,5 +1,9 @@
How to release pytest Release Procedure
-------------------------------------------- -----------------
Our current policy for releasing is to aim for a bugfix every few weeks and a minor release every 2-3 months. The idea
is to get fixes and new features out instead of trying to cram a ton of features into a release and by consequence
taking a lot of time to make a new one.
.. important:: .. important::
@ -21,7 +25,7 @@ How to release pytest
#. Generate docs, changelog, announcements and upload a package to #. Generate docs, changelog, announcements and upload a package to
your ``devpi`` staging server:: your ``devpi`` staging server::
invoke generate.pre_release <VERSION> <DEVPI USER> --password <DEVPI PASSWORD> invoke generate.pre-release <VERSION> <DEVPI USER> --password <DEVPI PASSWORD>
If ``--password`` is not given, it is assumed the user is already logged in ``devpi``. If ``--password`` is not given, it is assumed the user is already logged in ``devpi``.
If you don't have an account, please ask for one. If you don't have an account, please ask for one.
@ -49,7 +53,7 @@ How to release pytest
#. Publish to PyPI:: #. Publish to PyPI::
invoke generate.publish_release <VERSION> <DEVPI USER> <PYPI_NAME> invoke generate.publish-release <VERSION> <DEVPI USER> <PYPI_NAME>
where PYPI_NAME is the name of pypi.python.org as configured in your ``~/.pypirc`` where PYPI_NAME is the name of pypi.python.org as configured in your ``~/.pypirc``
file `for devpi <http://doc.devpi.net/latest/quickstart-releaseprocess.html?highlight=pypirc#devpi-push-releasing-to-an-external-index>`_. file `for devpi <http://doc.devpi.net/latest/quickstart-releaseprocess.html?highlight=pypirc#devpi-push-releasing-to-an-external-index>`_.

View File

@ -78,7 +78,8 @@ class FastFilesCompleter:
completion = [] completion = []
globbed = [] globbed = []
if '*' not in prefix and '?' not in prefix: if '*' not in prefix and '?' not in prefix:
if prefix[-1] == os.path.sep: # we are on unix, otherwise no bash # we are on unix, otherwise no bash
if not prefix or prefix[-1] == os.path.sep:
globbed.extend(glob(prefix + '.*')) globbed.extend(glob(prefix + '.*'))
prefix += '*' prefix += '*'
globbed.extend(glob(prefix)) globbed.extend(glob(prefix))
@ -98,7 +99,7 @@ if os.environ.get('_ARGCOMPLETE'):
filescompleter = FastFilesCompleter() filescompleter = FastFilesCompleter()
def try_argcomplete(parser): def try_argcomplete(parser):
argcomplete.autocomplete(parser) argcomplete.autocomplete(parser, always_complete_options=False)
else: else:
def try_argcomplete(parser): def try_argcomplete(parser):
pass pass

View File

@ -250,7 +250,7 @@ class TracebackEntry(object):
line = str(self.statement).lstrip() line = str(self.statement).lstrip()
except KeyboardInterrupt: except KeyboardInterrupt:
raise raise
except: except: # noqa
line = "???" line = "???"
return " File %r:%d in %s\n %s\n" % (fn, self.lineno + 1, name, line) return " File %r:%d in %s\n %s\n" % (fn, self.lineno + 1, name, line)
@ -338,16 +338,16 @@ class Traceback(list):
# XXX needs a test # XXX needs a test
key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno
# print "checking for recursion at", key # print "checking for recursion at", key
l = cache.setdefault(key, []) values = cache.setdefault(key, [])
if l: if values:
f = entry.frame f = entry.frame
loc = f.f_locals loc = f.f_locals
for otherloc in l: for otherloc in values:
if f.is_true(f.eval(co_equal, if f.is_true(f.eval(co_equal,
__recursioncache_locals_1=loc, __recursioncache_locals_1=loc,
__recursioncache_locals_2=otherloc)): __recursioncache_locals_2=otherloc)):
return i return i
l.append(entry.frame.f_locals) values.append(entry.frame.f_locals)
return None return None
@ -478,12 +478,12 @@ class FormattedExcinfo(object):
s = str(source.getstatement(len(source) - 1)) s = str(source.getstatement(len(source) - 1))
except KeyboardInterrupt: except KeyboardInterrupt:
raise raise
except: except: # noqa
try: try:
s = str(source[-1]) s = str(source[-1])
except KeyboardInterrupt: except KeyboardInterrupt:
raise raise
except: except: # noqa
return 0 return 0
return 4 + (len(s) - len(s.lstrip())) return 4 + (len(s) - len(s.lstrip()))

View File

@ -254,7 +254,7 @@ def findsource(obj):
sourcelines, lineno = py.std.inspect.findsource(obj) sourcelines, lineno = py.std.inspect.findsource(obj)
except py.builtin._sysex: except py.builtin._sysex:
raise raise
except: except: # noqa
return None, -1 return None, -1
source = Source() source = Source()
source.lines = [line.rstrip() for line in sourcelines] source.lines = [line.rstrip() for line in sourcelines]
@ -319,22 +319,22 @@ def get_statement_startend2(lineno, node):
import ast import ast
# flatten all statements and except handlers into one lineno-list # flatten all statements and except handlers into one lineno-list
# AST's line numbers start indexing at 1 # AST's line numbers start indexing at 1
l = [] values = []
for x in ast.walk(node): for x in ast.walk(node):
if isinstance(x, _ast.stmt) or isinstance(x, _ast.ExceptHandler): if isinstance(x, _ast.stmt) or isinstance(x, _ast.ExceptHandler):
l.append(x.lineno - 1) values.append(x.lineno - 1)
for name in "finalbody", "orelse": for name in "finalbody", "orelse":
val = getattr(x, name, None) val = getattr(x, name, None)
if val: if val:
# treat the finally/orelse part as its own statement # treat the finally/orelse part as its own statement
l.append(val[0].lineno - 1 - 1) values.append(val[0].lineno - 1 - 1)
l.sort() values.sort()
insert_index = bisect_right(l, lineno) insert_index = bisect_right(values, lineno)
start = l[insert_index - 1] start = values[insert_index - 1]
if insert_index >= len(l): if insert_index >= len(values):
end = None end = None
else: else:
end = l[insert_index] end = values[insert_index]
return start, end return start, end

View File

@ -210,7 +210,7 @@ class AssertionRewritingHook(object):
mod.__cached__ = pyc mod.__cached__ = pyc
mod.__loader__ = self mod.__loader__ = self
py.builtin.exec_(co, mod.__dict__) py.builtin.exec_(co, mod.__dict__)
except: except: # noqa
if name in sys.modules: if name in sys.modules:
del sys.modules[name] del sys.modules[name]
raise raise
@ -595,23 +595,26 @@ class AssertionRewriter(ast.NodeVisitor):
# docstrings and __future__ imports. # docstrings and __future__ imports.
aliases = [ast.alias(py.builtin.builtins.__name__, "@py_builtins"), aliases = [ast.alias(py.builtin.builtins.__name__, "@py_builtins"),
ast.alias("_pytest.assertion.rewrite", "@pytest_ar")] ast.alias("_pytest.assertion.rewrite", "@pytest_ar")]
expect_docstring = True doc = getattr(mod, "docstring", None)
expect_docstring = doc is None
if doc is not None and self.is_rewrite_disabled(doc):
return
pos = 0 pos = 0
lineno = 0 lineno = 1
for item in mod.body: for item in mod.body:
if (expect_docstring and isinstance(item, ast.Expr) and if (expect_docstring and isinstance(item, ast.Expr) and
isinstance(item.value, ast.Str)): isinstance(item.value, ast.Str)):
doc = item.value.s doc = item.value.s
if "PYTEST_DONT_REWRITE" in doc: if self.is_rewrite_disabled(doc):
# The module has disabled assertion rewriting.
return return
lineno += len(doc) - 1
expect_docstring = False expect_docstring = False
elif (not isinstance(item, ast.ImportFrom) or item.level > 0 or elif (not isinstance(item, ast.ImportFrom) or item.level > 0 or
item.module != "__future__"): item.module != "__future__"):
lineno = item.lineno lineno = item.lineno
break break
pos += 1 pos += 1
else:
lineno = item.lineno
imports = [ast.Import([alias], lineno=lineno, col_offset=0) imports = [ast.Import([alias], lineno=lineno, col_offset=0)
for alias in aliases] for alias in aliases]
mod.body[pos:pos] = imports mod.body[pos:pos] = imports
@ -637,6 +640,9 @@ class AssertionRewriter(ast.NodeVisitor):
not isinstance(field, ast.expr)): not isinstance(field, ast.expr)):
nodes.append(field) nodes.append(field)
def is_rewrite_disabled(self, docstring):
return "PYTEST_DONT_REWRITE" in docstring
def variable(self): def variable(self):
"""Get a new variable.""" """Get a new variable."""
# Use a character invalid in python identifiers to avoid clashing. # Use a character invalid in python identifiers to avoid clashing.

View File

@ -53,11 +53,11 @@ def _split_explanation(explanation):
""" """
raw_lines = (explanation or u('')).split('\n') raw_lines = (explanation or u('')).split('\n')
lines = [raw_lines[0]] lines = [raw_lines[0]]
for l in raw_lines[1:]: for values in raw_lines[1:]:
if l and l[0] in ['{', '}', '~', '>']: if values and values[0] in ['{', '}', '~', '>']:
lines.append(l) lines.append(values)
else: else:
lines[-1] += '\\n' + l lines[-1] += '\\n' + values
return lines return lines

View File

@ -1170,10 +1170,10 @@ class Config(object):
return [] return []
if type == "pathlist": if type == "pathlist":
dp = py.path.local(self.inicfg.config.path).dirpath() dp = py.path.local(self.inicfg.config.path).dirpath()
l = [] values = []
for relpath in shlex.split(value): for relpath in shlex.split(value):
l.append(dp.join(relpath, abs=True)) values.append(dp.join(relpath, abs=True))
return l return values
elif type == "args": elif type == "args":
return shlex.split(value) return shlex.split(value)
elif type == "linelist": elif type == "linelist":
@ -1190,13 +1190,13 @@ class Config(object):
except KeyError: except KeyError:
return None return None
modpath = py.path.local(mod.__file__).dirpath() modpath = py.path.local(mod.__file__).dirpath()
l = [] values = []
for relroot in relroots: for relroot in relroots:
if not isinstance(relroot, py.path.local): if not isinstance(relroot, py.path.local):
relroot = relroot.replace("/", py.path.local.sep) relroot = relroot.replace("/", py.path.local.sep)
relroot = modpath.join(relroot, abs=True) relroot = modpath.join(relroot, abs=True)
l.append(relroot) values.append(relroot)
return l return values
def _get_override_ini_value(self, name): def _get_override_ini_value(self, name):
value = None value = None

View File

@ -120,7 +120,7 @@ class DoctestItem(pytest.Item):
lines = ["%03d %s" % (i + test.lineno + 1, x) lines = ["%03d %s" % (i + test.lineno + 1, x)
for (i, x) in enumerate(lines)] for (i, x) in enumerate(lines)]
# trim docstring error lines to 10 # trim docstring error lines to 10
lines = lines[example.lineno - 9:example.lineno + 1] lines = lines[max(example.lineno - 9, 0):example.lineno + 1]
else: else:
lines = ['EXAMPLE LOCATION UNKNOWN, not showing all tests of that example'] lines = ['EXAMPLE LOCATION UNKNOWN, not showing all tests of that example']
indent = '>>>' indent = '>>>'

View File

@ -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
if sys.version_info[:2] == (2, 6): if sys.version_info[:2] == (2, 6):
from ordereddict import OrderedDict from ordereddict import OrderedDict
@ -458,13 +460,13 @@ class FixtureRequest(FuncargnamesCompatAttr):
def _get_fixturestack(self): def _get_fixturestack(self):
current = self current = self
l = [] values = []
while 1: while 1:
fixturedef = getattr(current, "_fixturedef", None) fixturedef = getattr(current, "_fixturedef", None)
if fixturedef is None: if fixturedef is None:
l.reverse() values.reverse()
return l return values
l.append(fixturedef) values.append(fixturedef)
current = current._parent_request current = current._parent_request
def _getfixturevalue(self, fixturedef): def _getfixturevalue(self, fixturedef):
@ -573,7 +575,6 @@ class SubRequest(FixtureRequest):
self.param_index = param_index self.param_index = param_index
self.scope = scope self.scope = scope
self._fixturedef = fixturedef self._fixturedef = fixturedef
self.addfinalizer = fixturedef.addfinalizer
self._pyfuncitem = request._pyfuncitem self._pyfuncitem = request._pyfuncitem
self._fixture_values = request._fixture_values self._fixture_values = request._fixture_values
self._fixture_defs = request._fixture_defs self._fixture_defs = request._fixture_defs
@ -584,6 +585,9 @@ class SubRequest(FixtureRequest):
def __repr__(self): def __repr__(self):
return "<SubRequest %r for %r>" % (self.fixturename, self._pyfuncitem) return "<SubRequest %r for %r>" % (self.fixturename, self._pyfuncitem)
def addfinalizer(self, finalizer):
self._fixturedef.addfinalizer(finalizer)
class ScopeMismatchError(Exception): class ScopeMismatchError(Exception):
""" A fixture function tries to use a different fixture function which """ A fixture function tries to use a different fixture function which
@ -747,7 +751,7 @@ class FixtureDef:
try: try:
func = self._finalizer.pop() func = self._finalizer.pop()
func() func()
except: except: # noqa
exceptions.append(sys.exc_info()) exceptions.append(sys.exc_info())
if exceptions: if exceptions:
e = exceptions[0] e = exceptions[0]
@ -981,8 +985,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):
@ -1037,8 +1041,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()]
@ -1127,5 +1136,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

View File

@ -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

View File

@ -6,6 +6,7 @@ import os
import sys import sys
import _pytest import _pytest
from _pytest import nodes
import _pytest._code import _pytest._code
import py import py
try: try:
@ -14,8 +15,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()
@ -117,7 +118,7 @@ def wrap_session(config, doit):
excinfo.typename, excinfo.value.msg)) excinfo.typename, excinfo.value.msg))
config.hook.pytest_keyboard_interrupt(excinfo=excinfo) config.hook.pytest_keyboard_interrupt(excinfo=excinfo)
session.exitstatus = EXIT_INTERRUPTED session.exitstatus = EXIT_INTERRUPTED
except: except: # noqa
excinfo = _pytest._code.ExceptionInfo() excinfo = _pytest._code.ExceptionInfo()
config.notify_exception(excinfo, config.option) config.notify_exception(excinfo, config.option)
session.exitstatus = EXIT_INTERNALERROR session.exitstatus = EXIT_INTERNALERROR
@ -374,7 +375,7 @@ class Node(object):
res = function() res = function()
except py.builtin._sysex: except py.builtin._sysex:
raise raise
except: except: # noqa
failure = sys.exc_info() failure = sys.exc_info()
setattr(self, exattrname, failure) setattr(self, exattrname, failure)
raise raise
@ -516,7 +517,7 @@ 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
@ -527,10 +528,11 @@ class FSCollector(Collector):
def _makeid(self): def _makeid(self):
relpath = self.fspath.relto(self.config.rootdir) relpath = self.fspath.relto(self.config.rootdir)
if not relpath: if not relpath:
relpath = self._check_initialpaths_for_relpath() relpath = self._check_initialpaths_for_relpath()
if os.sep != "/": if os.sep != nodes.SEP:
relpath = relpath.replace(os.sep, "/") relpath = relpath.replace(os.sep, nodes.SEP)
return relpath return relpath

View File

@ -91,7 +91,8 @@ def pytest_addoption(parser):
"where all names are substring-matched against test names " "where all names are substring-matched against test names "
"and their parent classes. Example: -k 'test_method or test_" "and their parent classes. Example: -k 'test_method or test_"
"other' matches all test functions and classes whose name " "other' matches all test functions and classes whose name "
"contains 'test_method' or 'test_other'. " "contains 'test_method' or 'test_other', while -k 'not test_method' "
"matches those that don't contain 'test_method' in their names. "
"Additionally keywords are matched to classes and functions " "Additionally keywords are matched to classes and functions "
"containing extra names in their 'extra_keyword_matches' set, " "containing extra names in their 'extra_keyword_matches' set, "
"as well as functions which have names assigned directly to them." "as well as functions which have names assigned directly to them."
@ -269,11 +270,12 @@ class MarkGenerator:
return return
except AttributeError: except AttributeError:
pass pass
self._markers = l = set() self._markers = values = set()
for line in self._config.getini("markers"): for line in self._config.getini("markers"):
beginning = line.split(":", 1) marker, _ = line.split(":", 1)
x = beginning[0].split("(", 1)[0] marker = marker.rstrip()
l.add(x) x = marker.split("(", 1)[0]
values.add(x)
if name not in self._markers: if name not in self._markers:
raise AttributeError("%r not a registered marker" % (name,)) raise AttributeError("%r not a registered marker" % (name,))
@ -382,7 +384,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]

37
_pytest/nodes.py Normal file
View File

@ -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

View File

@ -182,9 +182,9 @@ class PytestArg:
return hookrecorder return hookrecorder
def get_public_names(l): def get_public_names(values):
"""Only return names from iterator l without a leading underscore.""" """Only return names from iterator values without a leading underscore."""
return [x for x in l if x[0] != "_"] return [x for x in values if x[0] != "_"]
class ParsedCall: class ParsedCall:
@ -258,9 +258,9 @@ class HookRecorder:
pytest.fail("\n".join(lines)) pytest.fail("\n".join(lines))
def getcall(self, name): def getcall(self, name):
l = self.getcalls(name) values = self.getcalls(name)
assert len(l) == 1, (name, l) assert len(values) == 1, (name, values)
return l[0] return values[0]
# functionality for test reports # functionality for test reports
@ -271,7 +271,7 @@ class HookRecorder:
def matchreport(self, inamepart="", def matchreport(self, inamepart="",
names="pytest_runtest_logreport pytest_collectreport", when=None): names="pytest_runtest_logreport pytest_collectreport", when=None):
""" return a testreport whose dotted import path matches """ """ return a testreport whose dotted import path matches """
l = [] values = []
for rep in self.getreports(names=names): for rep in self.getreports(names=names):
try: try:
if not when and rep.when != "call" and rep.passed: if not when and rep.when != "call" and rep.passed:
@ -282,14 +282,14 @@ class HookRecorder:
if when and getattr(rep, 'when', None) != when: if when and getattr(rep, 'when', None) != when:
continue continue
if not inamepart or inamepart in rep.nodeid.split("::"): if not inamepart or inamepart in rep.nodeid.split("::"):
l.append(rep) values.append(rep)
if not l: if not values:
raise ValueError("could not find test report matching %r: " raise ValueError("could not find test report matching %r: "
"no test reports at all!" % (inamepart,)) "no test reports at all!" % (inamepart,))
if len(l) > 1: if len(values) > 1:
raise ValueError( raise ValueError(
"found 2 or more testreports matching %r: %s" % (inamepart, l)) "found 2 or more testreports matching %r: %s" % (inamepart, values))
return l[0] return values[0]
def getfailures(self, def getfailures(self,
names='pytest_runtest_logreport pytest_collectreport'): names='pytest_runtest_logreport pytest_collectreport'):
@ -673,8 +673,8 @@ class Testdir:
""" """
p = self.makepyfile(source) p = self.makepyfile(source)
l = list(cmdlineargs) + [p] values = list(cmdlineargs) + [p]
return self.inline_run(*l) return self.inline_run(*values)
def inline_genitems(self, *args): def inline_genitems(self, *args):
"""Run ``pytest.main(['--collectonly'])`` in-process. """Run ``pytest.main(['--collectonly'])`` in-process.

View File

@ -321,7 +321,7 @@ class PyCollector(PyobjMixin, main.Collector):
for basecls in inspect.getmro(self.obj.__class__): for basecls in inspect.getmro(self.obj.__class__):
dicts.append(basecls.__dict__) dicts.append(basecls.__dict__)
seen = {} seen = {}
l = [] values = []
for dic in dicts: for dic in dicts:
for name, obj in list(dic.items()): for name, obj in list(dic.items()):
if name in seen: if name in seen:
@ -332,9 +332,9 @@ class PyCollector(PyobjMixin, main.Collector):
continue continue
if not isinstance(res, list): if not isinstance(res, list):
res = [res] res = [res]
l.extend(res) values.extend(res)
l.sort(key=lambda item: item.reportinfo()[:2]) values.sort(key=lambda item: item.reportinfo()[:2])
return l return values
def makeitem(self, name, obj): def makeitem(self, name, obj):
# assert self.ihook.fspath == self.fspath, self # assert self.ihook.fspath == self.fspath, self
@ -592,7 +592,7 @@ class Generator(FunctionMixin, PyCollector):
self.session._setupstate.prepare(self) self.session._setupstate.prepare(self)
# see FunctionMixin.setup and test_setupstate_is_preserved_134 # see FunctionMixin.setup and test_setupstate_is_preserved_134
self._preservedparent = self.parent.obj self._preservedparent = self.parent.obj
l = [] values = []
seen = {} seen = {}
for i, x in enumerate(self.obj()): for i, x in enumerate(self.obj()):
name, call, args = self.getcallargs(x) name, call, args = self.getcallargs(x)
@ -605,9 +605,9 @@ class Generator(FunctionMixin, PyCollector):
if name in seen: if name in seen:
raise ValueError("%r generated tests with non-unique name %r" % (self, name)) raise ValueError("%r generated tests with non-unique name %r" % (self, name))
seen[name] = True seen[name] = True
l.append(self.Function(name, self, args=args, callobj=call)) values.append(self.Function(name, self, args=args, callobj=call))
self.warn('C1', deprecated.YIELD_TESTS) self.warn('C1', deprecated.YIELD_TESTS)
return l return values
def getcallargs(self, obj): def getcallargs(self, obj):
if not isinstance(obj, (tuple, list)): if not isinstance(obj, (tuple, list)):
@ -979,50 +979,48 @@ def _show_fixtures_per_test(config, session):
tw = _pytest.config.create_terminal_writer(config) tw = _pytest.config.create_terminal_writer(config)
verbose = config.getvalue("verbose") verbose = config.getvalue("verbose")
def get_best_rel(func): def get_best_relpath(func):
loc = getlocation(func, curdir) loc = getlocation(func, curdir)
return curdir.bestrelpath(loc) return curdir.bestrelpath(loc)
def write_fixture(fixture_def): def write_fixture(fixture_def):
argname = fixture_def.argname argname = fixture_def.argname
if verbose <= 0 and argname.startswith("_"): if verbose <= 0 and argname.startswith("_"):
return return
if verbose > 0: if verbose > 0:
bestrel = get_best_rel(fixture_def.func) bestrel = get_best_relpath(fixture_def.func)
funcargspec = "{0} -- {1}".format(argname, bestrel) funcargspec = "{0} -- {1}".format(argname, bestrel)
else: else:
funcargspec = argname funcargspec = argname
tw.line(funcargspec, green=True) tw.line(funcargspec, green=True)
fixture_doc = fixture_def.func.__doc__ fixture_doc = fixture_def.func.__doc__
if fixture_doc: if fixture_doc:
write_docstring(tw, fixture_doc) write_docstring(tw, fixture_doc)
else: else:
tw.line(' no docstring available', red=True) tw.line(' no docstring available', red=True)
def write_item(item): def write_item(item):
name2fixturedefs = item._fixtureinfo.name2fixturedefs try:
info = item._fixtureinfo
if not name2fixturedefs: except AttributeError:
# The given test item does not use any fixtures # doctests items have no _fixtureinfo attribute
return
if not info.name2fixturedefs:
# this test item does not use any fixtures
return return
bestrel = get_best_rel(item.function)
tw.line() tw.line()
tw.sep('-', 'fixtures used by {0}'.format(item.name)) tw.sep('-', 'fixtures used by {0}'.format(item.name))
tw.sep('-', '({0})'.format(bestrel)) tw.sep('-', '({0})'.format(get_best_relpath(item.function)))
for argname, fixture_defs in sorted(name2fixturedefs.items()): # dict key not used in loop but needed for sorting
assert fixture_defs is not None for _, fixturedefs in sorted(info.name2fixturedefs.items()):
if not fixture_defs: assert fixturedefs is not None
if not fixturedefs:
continue continue
# The last fixture def item in the list is expected # last item is expected to be the one used by the test item
# to be the one used by the test item write_fixture(fixturedefs[-1])
write_fixture(fixture_defs[-1])
for item in session.items: for session_item in session.items:
write_item(item) write_item(session_item)
def showfixtures(config): def showfixtures(config):

View File

@ -84,7 +84,7 @@ class ApproxNumpy(ApproxBase):
try: try:
actual = np.asarray(actual) actual = np.asarray(actual)
except: except: # noqa
raise TypeError("cannot compare '{0}' to numpy.ndarray".format(actual)) raise TypeError("cannot compare '{0}' to numpy.ndarray".format(actual))
if actual.shape != self.expected.shape: if actual.shape != self.expected.shape:
@ -217,7 +217,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.

View File

@ -56,11 +56,6 @@ def pytest_sessionfinish(session):
session._setupstate.teardown_all() session._setupstate.teardown_all()
class NodeInfo:
def __init__(self, location):
self.location = location
def pytest_runtest_protocol(item, nextitem): def pytest_runtest_protocol(item, nextitem):
item.ihook.pytest_runtest_logstart( item.ihook.pytest_runtest_logstart(
nodeid=item.nodeid, location=item.location, nodeid=item.nodeid, location=item.location,
@ -197,7 +192,7 @@ class CallInfo:
except KeyboardInterrupt: except KeyboardInterrupt:
self.stop = time() self.stop = time()
raise raise
except: except: # noqa
self.excinfo = ExceptionInfo() self.excinfo = ExceptionInfo()
self.stop = time() self.stop = time()

View File

@ -346,10 +346,10 @@ def folded_skips(skipped):
key = event.longrepr key = event.longrepr
assert len(key) == 3, (event, key) assert len(key) == 3, (event, key)
d.setdefault(key, []).append(event) d.setdefault(key, []).append(event)
l = [] values = []
for key, events in d.items(): for key, events in d.items():
l.append((len(events),) + key) values.append((len(events),) + key)
return l return values
def show_skipped(terminalreporter, lines): def show_skipped(terminalreporter, lines):

View File

@ -13,6 +13,7 @@ import sys
import time import time
import platform import platform
from _pytest import nodes
import _pytest._pluggy as pluggy import _pytest._pluggy as pluggy
@ -444,15 +445,15 @@ class TerminalReporter:
line = self.config.cwd_relative_nodeid(nodeid) line = self.config.cwd_relative_nodeid(nodeid)
if domain and line.endswith(domain): if domain and line.endswith(domain):
line = line[:-len(domain)] line = line[:-len(domain)]
l = domain.split("[") values = domain.split("[")
l[0] = l[0].replace('.', '::') # don't replace '.' in params values[0] = values[0].replace('.', '::') # don't replace '.' in params
line += "[".join(l) line += "[".join(values)
return line return line
# collect_fspath comes from testid which has a "/"-normalized path # collect_fspath comes from testid which has a "/"-normalized path
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]"
@ -478,11 +479,11 @@ class TerminalReporter:
# summaries for sessionfinish # summaries for sessionfinish
# #
def getreports(self, name): def getreports(self, name):
l = [] values = []
for x in self.stats.get(name, []): for x in self.stats.get(name, []):
if not hasattr(x, '_pdbshown'): if not hasattr(x, '_pdbshown'):
l.append(x) values.append(x)
return l return values
def summary_warnings(self): def summary_warnings(self):
if self.hasopt("w"): if self.hasopt("w"):
@ -593,8 +594,8 @@ def repr_pythonversion(v=None):
return str(v) return str(v)
def flatten(l): def flatten(values):
for x in l: for x in values:
if isinstance(x, (list, tuple)): if isinstance(x, (list, tuple)):
for y in flatten(x): for y in flatten(x):
yield y yield y
@ -635,7 +636,7 @@ def build_summary_stats_line(stats):
def _plugin_nameversions(plugininfo): def _plugin_nameversions(plugininfo):
l = [] values = []
for plugin, dist in plugininfo: for plugin, dist in plugininfo:
# gets us name and version! # gets us name and version!
name = '{dist.project_name}-{dist.version}'.format(dist=dist) name = '{dist.project_name}-{dist.version}'.format(dist=dist)
@ -644,6 +645,6 @@ def _plugin_nameversions(plugininfo):
name = name[7:] name = name[7:]
# we decided to print python package names # we decided to print python package names
# they can have more than one plugin # they can have more than one plugin
if name not in l: if name not in values:
l.append(name) values.append(name)
return l return values

View File

@ -109,13 +109,13 @@ class TestCaseFunction(Function):
except TypeError: except TypeError:
try: try:
try: try:
l = traceback.format_exception(*rawexcinfo) values = traceback.format_exception(*rawexcinfo)
l.insert(0, "NOTE: Incompatible Exception Representation, " values.insert(0, "NOTE: Incompatible Exception Representation, "
"displaying natively:\n\n") "displaying natively:\n\n")
fail("".join(l), pytrace=False) fail("".join(values), pytrace=False)
except (fail.Exception, KeyboardInterrupt): except (fail.Exception, KeyboardInterrupt):
raise raise
except: except: # noqa
fail("ERROR: Unknown Incompatible Exception " fail("ERROR: Unknown Incompatible Exception "
"representation:\n%r" % (rawexcinfo,), pytrace=False) "representation:\n%r" % (rawexcinfo,), pytrace=False)
except KeyboardInterrupt: except KeyboardInterrupt:

1
changelog/1505.doc Normal file
View File

@ -0,0 +1 @@
Introduce a dedicated section about conftest.py.

View File

@ -1 +0,0 @@
Add note in ``parametrize.rst`` about calling ``metafunc.parametrize`` multiple times.

1
changelog/1997.doc Normal file
View File

@ -0,0 +1 @@
Explicitly mention ``xpass`` in the documentation of ``xfail``.

1
changelog/2658.doc Normal file
View File

@ -0,0 +1 @@
Append example for pytest.param in the example/parametrize document.

View File

@ -1 +0,0 @@
Set ``xfail_strict=True`` in pytest's own test suite to catch expected failures as soon as they start to pass.

View File

@ -1 +0,0 @@
The equality checking function (``__eq__``) of ``MarkDecorator`` returns ``False`` if one object is not an instance of ``MarkDecorator``.

View File

@ -1 +0,0 @@
Fix typo in example of passing a callable to markers (in example/markers.rst)

1
changelog/2819.bugfix Normal file
View File

@ -0,0 +1 @@
Fix issue with @pytest.parametrize if argnames was specified as kwarg.

1
changelog/2836.bug Normal file
View File

@ -0,0 +1 @@
Match fixture paths against actual path segments in order to avoid matching folders which share a prefix.

1
changelog/2856.bugfix Normal file
View File

@ -0,0 +1 @@
Strip whitespace from marker names when reading them from INI config.

1
changelog/2882.bugfix Normal file
View File

@ -0,0 +1 @@
Show full context of doctest source in the pytest output, if the lineno of failed example in the docstring is < 9.

1
changelog/2893.doc Normal file
View File

@ -0,0 +1 @@
Clarify language of proposal for fixtures parameters

1
changelog/2903.doc Normal file
View File

@ -0,0 +1 @@
List python 3.6 in the documented supported versions in the getting started document.

1
changelog/538.doc Normal file
View File

@ -0,0 +1 @@
Clarify the documentation of available fixture scopes.

1
changelog/911.doc Normal file
View File

@ -0,0 +1 @@
Add documentation about the ``python -m pytest`` invocation adding the current directory to sys.path.

View File

@ -6,6 +6,7 @@ Release announcements
:maxdepth: 2 :maxdepth: 2
release-3.2.3
release-3.2.2 release-3.2.2
release-3.2.1 release-3.2.1
release-3.2.0 release-3.2.0

View File

@ -0,0 +1,23 @@
pytest-3.2.3
=======================================
pytest 3.2.3 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Bruno Oliveira
* Evan
* Joe Hamman
* Oliver Bestwalter
* Ronny Pfannschmidt
* Xuan Luong
Happy testing,
The pytest Development Team

View File

@ -209,8 +209,8 @@ the ``pytest_assertrepr_compare`` hook.
.. autofunction:: _pytest.hookspec.pytest_assertrepr_compare .. autofunction:: _pytest.hookspec.pytest_assertrepr_compare
:noindex: :noindex:
As an example consider adding the following hook in a conftest.py which As an example consider adding the following hook in a :ref:`conftest.py <conftest.py>`
provides an alternative explanation for ``Foo`` objects:: file which provides an alternative explanation for ``Foo`` objects::
# content of conftest.py # content of conftest.py
from test_foocompare import Foo from test_foocompare import Foo

View File

@ -41,6 +41,7 @@ Full pytest documentation
historical-notes historical-notes
license license
contributing contributing
development_guide
talks talks
projects projects
faq faq

View File

@ -0,0 +1,108 @@
=================
Development Guide
=================
Some general guidelines regarding development in pytest for core maintainers and general contributors. Nothing here
is set in stone and can't be changed, feel free to suggest improvements or changes in the workflow.
Code Style
----------
* `PEP-8 <https://www.python.org/dev/peps/pep-0008>`_
* `flake8 <https://pypi.python.org/pypi/flake8>`_ for quality checks
* `invoke <http://www.pyinvoke.org/>`_ to automate development tasks
Branches
--------
We have two long term branches:
* ``master``: contains the code for the next bugfix release.
* ``features``: contains the code with new features for the next minor release.
The official repository usually does not contain topic branches, developers and contributors should create topic
branches in their own forks.
Exceptions can be made for cases where more than one contributor is working on the same
topic or where it makes sense to use some automatic capability of the main repository, such as automatic docs from
`readthedocs <readthedocs.org>`_ for a branch dealing with documentation refactoring.
Issues
------
Any question, feature, bug or proposal is welcome as an issue. Users are encouraged to use them whenever they need.
GitHub issues should use labels to categorize them. Labels should be created sporadically, to fill a niche; we should
avoid creating labels just for the sake of creating them.
Here is a list of labels and a brief description mentioning their intent.
**Type**
* ``type: backward compatibility``: issue that will cause problems with old pytest versions.
* ``type: bug``: problem that needs to be addressed.
* ``type: deprecation``: feature that will be deprecated in the future.
* ``type: docs``: documentation missing or needing clarification.
* ``type: enhancement``: new feature or API change, should be merged into ``features``.
* ``type: feature-branch``: new feature or API change, should be merged into ``features``.
* ``type: infrastructure``: improvement to development/releases/CI structure.
* ``type: performance``: performance or memory problem/improvement.
* ``type: proposal``: proposal for a new feature, often to gather opinions or design the API around the new feature.
* ``type: question``: question regarding usage, installation, internals or how to test something.
* ``type: refactoring``: internal improvements to the code.
* ``type: regression``: indicates a problem that was introduced in a release which was working previously.
**Status**
* ``status: critical``: grave problem or usability issue that affects lots of users.
* ``status: easy``: easy issue that is friendly to new contributors.
* ``status: help wanted``: core developers need help from experts on this topic.
* ``status: needs information``: reporter needs to provide more information; can be closed after 2 or more weeks of inactivity.
**Topic**
* ``topic: collection``
* ``topic: fixtures``
* ``topic: parametrize``
* ``topic: reporting``
* ``topic: selection``
* ``topic: tracebacks``
**Plugin (internal or external)**
* ``plugin: cache``
* ``plugin: capture``
* ``plugin: doctests``
* ``plugin: junitxml``
* ``plugin: monkeypatch``
* ``plugin: nose``
* ``plugin: pastebin``
* ``plugin: pytester``
* ``plugin: tmpdir``
* ``plugin: unittest``
* ``plugin: warnings``
* ``plugin: xdist``
**OS**
Issues specific to a single operating system. Do not use as a means to indicate where an issue originated from, only
for problems that happen **only** in that system.
* ``os: linux``
* ``os: mac``
* ``os: windows``
**Temporary**
Used to classify issues for limited time, to help find issues related in events for example.
They should be removed after they are no longer relevant.
* ``temporary: EP2017 sprint``:
* ``temporary: sprint-candidate``:
.. include:: ../../HOWTORELEASE.rst

View File

@ -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::
@ -485,4 +485,54 @@ of our ``test_func1`` was skipped. A few notes:
values as well. values as well.
Set marks or test ID for individual parametrized test
--------------------------------------------------------------------
Use ``pytest.param`` to apply marks or set test ID to individual parametrized test.
For example::
# content of test_pytest_param_example.py
import pytest
@pytest.mark.parametrize('test_input,expected', [
('3+5', 8),
pytest.param('1+7', 8,
marks=pytest.mark.basic),
pytest.param('2+4', 6,
marks=pytest.mark.basic,
id='basic_2+4'),
pytest.param('6*9', 42,
marks=[pytest.mark.basic, pytest.mark.xfail],
id='basic_6*9'),
])
def test_eval(test_input, expected):
assert eval(test_input) == expected
In this example, we have 4 parametrized tests. Except for the first test,
we mark the rest three parametrized tests with the custom marker ``basic``,
and for the fourth test we also use the built-in mark ``xfail`` to indicate this
test is expected to fail. For explicitness, we set test ids for some tests.
Then run ``pytest`` with verbose mode and with only the ``basic`` marker::
pytest -v -m basic
============================================ test session starts =============================================
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items
test_pytest_param_example.py::test_eval[1+7-8] PASSED
test_pytest_param_example.py::test_eval[basic_2+4] PASSED
test_pytest_param_example.py::test_eval[basic_6*9] xfail
========================================== short test summary info ===========================================
XFAIL test_pytest_param_example.py::test_eval[basic_6*9]
============================================= 1 tests deselected =============================================
As the result:
- Four tests were collected
- One test was deselected because it doesn't have the ``basic`` mark.
- Three tests with the ``basic`` mark was selected.
- The test ``test_eval[1+7-8]`` passed, but the name is autogenerated and confusing.
- The test ``test_eval[basic_2+4]`` passed.
- The test ``test_eval[basic_6*9]`` was expected to fail and did fail.

View File

@ -175,21 +175,23 @@ You can always peek at the collection tree without running tests like this::
======= no tests ran in 0.12 seconds ======== ======= no tests ran in 0.12 seconds ========
customizing test collection to find all .py files .. _customizing-test-collection:
---------------------------------------------------------
Customizing test collection
---------------------------
.. regendoc:wipe .. regendoc:wipe
You can easily instruct ``pytest`` to discover tests from every python file:: You can easily instruct ``pytest`` to discover tests from every Python file::
# content of pytest.ini # content of pytest.ini
[pytest] [pytest]
python_files = *.py python_files = *.py
However, many projects will have a ``setup.py`` which they don't want to be imported. Moreover, there may files only importable by a specific python version. However, many projects will have a ``setup.py`` which they don't want to be
For such cases you can dynamically define files to be ignored by listing imported. Moreover, there may files only importable by a specific python
them in a ``conftest.py`` file:: version. For such cases you can dynamically define files to be ignored by
listing them in a ``conftest.py`` file::
# content of conftest.py # content of conftest.py
import sys import sys
@ -198,7 +200,7 @@ them in a ``conftest.py`` file::
if sys.version_info[0] > 2: if sys.version_info[0] > 2:
collect_ignore.append("pkg/module_py2.py") collect_ignore.append("pkg/module_py2.py")
And then if you have a module file like this:: and then if you have a module file like this::
# content of pkg/module_py2.py # content of pkg/module_py2.py
def test_only_on_python2(): def test_only_on_python2():
@ -207,13 +209,13 @@ And then if you have a module file like this::
except Exception, e: except Exception, e:
pass pass
and a setup.py dummy file like this:: and a ``setup.py`` dummy file like this::
# content of setup.py # content of setup.py
0/0 # will raise exception if imported 0/0 # will raise exception if imported
then a pytest run on Python2 will find the one test and will leave out the If you run with a Python 2 interpreter then you will find the one test and will
setup.py file:: leave out the ``setup.py`` file::
#$ pytest --collect-only #$ pytest --collect-only
====== test session starts ====== ====== test session starts ======
@ -225,8 +227,8 @@ setup.py file::
====== no tests ran in 0.04 seconds ====== ====== no tests ran in 0.04 seconds ======
If you run with a Python3 interpreter both the one test and the setup.py file If you run with a Python 3 interpreter both the one test and the ``setup.py``
will be left out:: file will be left out::
$ pytest --collect-only $ pytest --collect-only
======= test session starts ======== ======= test session starts ========

View File

@ -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
@ -127,10 +127,39 @@ It's a prime example of `dependency injection`_ where fixture
functions take the role of the *injector* and test functions are the functions take the role of the *injector* and test functions are the
*consumers* of fixture objects. *consumers* of fixture objects.
.. _`conftest.py`:
.. _`conftest`:
``conftest.py``: sharing fixture functions
------------------------------------------
If during implementing your tests you realize that you
want to use a fixture function from multiple test files you can move it
to a ``conftest.py`` file.
You don't need to import the fixture you want to use in a test, it
automatically gets discovered by pytest. The discovery of
fixture functions starts at test classes, then test modules, then
``conftest.py`` files and finally builtin and third party plugins.
You can also use the ``conftest.py`` file to implement
:ref:`local per-directory plugins <conftest.py plugins>`.
Sharing test data
-----------------
If you want to make test data from files available to your tests, a good way
to do this is by loading these data in a fixture for use by your tests.
This makes use of the automatic caching mechanisms of pytest.
Another good approach is by adding the data files in the ``tests`` folder.
There are also community plugins available to help managing this aspect of
testing, e.g. `pytest-datadir <https://github.com/gabrielcnr/pytest-datadir>`__
and `pytest-datafiles <https://pypi.python.org/pypi/pytest-datafiles>`__.
.. _smtpshared: .. _smtpshared:
Sharing a fixture across tests in a module (or class/session) Scope: sharing a fixture instance across tests in a class, module or session
----------------------------------------------------------------- ----------------------------------------------------------------------------
.. regendoc:wipe .. regendoc:wipe
@ -139,10 +168,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 +254,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 +891,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()
@ -874,17 +907,6 @@ All test methods in this TestClass will use the transaction fixture while
other test classes or functions in the module will not use it unless other test classes or functions in the module will not use it unless
they also add a ``transact`` reference. they also add a ``transact`` reference.
Shifting (visibility of) fixture functions
----------------------------------------------------
If during implementing your tests you realize that you
want to use a fixture function from multiple test files you can move it
to a :ref:`conftest.py <conftest.py>` file or even separately installable
:ref:`plugins <plugins>` without changing test code. The discovery of
fixtures functions starts at test classes, then test modules, then
``conftest.py`` files and finally builtin and third party plugins.
Overriding fixtures on various levels Overriding fixtures on various levels
------------------------------------- -------------------------------------

View File

@ -1,7 +1,7 @@
Installation and Getting Started Installation and Getting Started
=================================== ===================================
**Pythons**: Python 2.6,2.7,3.3,3.4,3.5, Jython, PyPy-2.3 **Pythons**: Python 2.6,2.7,3.3,3.4,3.5,3.6 Jython, PyPy-2.3
**Platforms**: Unix/Posix and Windows **Platforms**: Unix/Posix and Windows

View File

@ -198,10 +198,12 @@ list::
SKIP [1] test_strings.py:2: got empty parameter set ['stringinput'], function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:1 SKIP [1] test_strings.py:2: got empty parameter set ['stringinput'], function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:1
1 skipped in 0.12 seconds 1 skipped in 0.12 seconds
Note that when calling ``metafunc.parametrize`` multiple times with different parameter sets, all parameter names across Note that when calling ``metafunc.parametrize`` multiple times with different parameter sets, all parameter names across
those sets cannot be duplicated, otherwise an error will be raised. those sets cannot be duplicated, otherwise an error will be raised.
More examples
-------------
For further examples, you might want to look at :ref:`more For further examples, you might want to look at :ref:`more
parametrization examples <paramexamples>`. parametrization examples <paramexamples>`.

View File

@ -94,7 +94,7 @@ environment you can type::
and will get an extended test header which shows activated plugins and will get an extended test header which shows activated plugins
and their names. It will also print local plugins aka and their names. It will also print local plugins aka
:ref:`conftest.py <conftest>` files when they are loaded. :ref:`conftest.py <conftest.py plugins>` files when they are loaded.
.. _`cmdunregister`: .. _`cmdunregister`:
@ -155,4 +155,3 @@ in the `pytest repository <https://github.com/pytest-dev/pytest>`_.
_pytest.terminal _pytest.terminal
_pytest.tmpdir _pytest.tmpdir
_pytest.unittest _pytest.unittest

View File

@ -1,8 +1,13 @@
:orphan: :orphan:
========================= ===================================
Parametrize with fixtures PROPOSAL: Parametrize with fixtures
========================= ===================================
.. warning::
This document outlines a proposal around using fixtures as input
of parametrized tests or fixtures.
Problem Problem
------- -------
@ -108,8 +113,13 @@ the following values.
Alternative approach Alternative approach
-------------------- --------------------
A new helper function named ``fixture_request`` tells pytest to yield all A new helper function named ``fixture_request`` would tell pytest to yield
parameters of a fixture. all parameters marked as a fixture.
.. note::
The `pytest-lazy-fixture <https://pypi.python.org/pypi/pytest-lazy-fixture>`_ plugin implements a very
similar solution to the proposal below, make sure to check it out.
.. code-block:: python .. code-block:: python

View File

@ -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`.

View File

@ -3,7 +3,7 @@
.. _skipping: .. _skipping:
Skip and xfail: dealing with tests that cannot succeed Skip and xfail: dealing with tests that cannot succeed
===================================================================== ======================================================
You can mark test functions that cannot be run on certain platforms You can mark test functions that cannot be run on certain platforms
or that you expect to fail so pytest can deal with them accordingly and or that you expect to fail so pytest can deal with them accordingly and
@ -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`)
@ -138,6 +142,16 @@ will be skipped if any of the skip conditions is true.
.. _`whole class- or module level`: mark.html#scoped-marking .. _`whole class- or module level`: mark.html#scoped-marking
Skipping files or directories
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sometimes you may need to skip an entire file or directory, for example if the
tests rely on Python version-specific features or contain code that you do not
wish pytest to run. In this case, you must exclude the files and directories
from collection. Refer to :ref:`customizing-test-collection` for more
information.
Skipping on a missing import dependency Skipping on a missing import dependency
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -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``.

View File

@ -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
-------------------------------------------------------------- --------------------------------------------------------------

View File

@ -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::

View File

@ -57,9 +57,7 @@ Plugin discovery order at tool startup
.. _`pytest/plugin`: http://bitbucket.org/pytest-dev/pytest/src/tip/pytest/plugin/ .. _`pytest/plugin`: http://bitbucket.org/pytest-dev/pytest/src/tip/pytest/plugin/
.. _`conftest.py plugins`: .. _`conftest.py plugins`:
.. _`conftest.py`:
.. _`localplugin`: .. _`localplugin`:
.. _`conftest`:
.. _`local conftest plugins`: .. _`local conftest plugins`:
conftest.py: local per-directory plugins conftest.py: local per-directory plugins

View File

@ -77,8 +77,8 @@ def test_excinfo_getstatement():
linenumbers = [_pytest._code.getrawcode(f).co_firstlineno - 1 + 4, linenumbers = [_pytest._code.getrawcode(f).co_firstlineno - 1 + 4,
_pytest._code.getrawcode(f).co_firstlineno - 1 + 1, _pytest._code.getrawcode(f).co_firstlineno - 1 + 1,
_pytest._code.getrawcode(g).co_firstlineno - 1 + 1, ] _pytest._code.getrawcode(g).co_firstlineno - 1 + 1, ]
l = list(excinfo.traceback) values = list(excinfo.traceback)
foundlinenumbers = [x.lineno for x in l] foundlinenumbers = [x.lineno for x in values]
assert foundlinenumbers == linenumbers assert foundlinenumbers == linenumbers
# for x in info: # for x in info:
# print "%s:%d %s" %(x.path.relto(root), x.lineno, x.statement) # print "%s:%d %s" %(x.path.relto(root), x.lineno, x.statement)
@ -244,7 +244,7 @@ class TestTraceback_f_g_h(object):
def f(n): def f(n):
try: try:
do_stuff() do_stuff()
except: except: # noqa
reraise_me() reraise_me()
excinfo = pytest.raises(RuntimeError, f, 8) excinfo = pytest.raises(RuntimeError, f, 8)
@ -434,7 +434,7 @@ class TestFormattedExcinfo(object):
exec(source.compile()) exec(source.compile())
except KeyboardInterrupt: except KeyboardInterrupt:
raise raise
except: except: # noqa
return _pytest._code.ExceptionInfo() return _pytest._code.ExceptionInfo()
assert 0, "did not raise" assert 0, "did not raise"
@ -1217,7 +1217,7 @@ def test_exception_repr_extraction_error_on_recursion():
try: try:
a(numpy_like()) a(numpy_like())
except: except: # noqa
from _pytest._code.code import ExceptionInfo from _pytest._code.code import ExceptionInfo
from _pytest.pytester import LineMatcher from _pytest.pytester import LineMatcher
exc_info = ExceptionInfo() exc_info = ExceptionInfo()
@ -1241,7 +1241,7 @@ def test_no_recursion_index_on_recursion_error():
return getattr(self, '_' + attr) return getattr(self, '_' + attr)
RecursionDepthError().trigger RecursionDepthError().trigger
except: except: # noqa
from _pytest._code.code import ExceptionInfo from _pytest._code.code import ExceptionInfo
exc_info = ExceptionInfo() exc_info = ExceptionInfo()
if sys.version_info[:2] == (2, 6): if sys.version_info[:2] == (2, 6):

View File

@ -155,8 +155,8 @@ class TestAccesses(object):
assert len(self.source) == 4 assert len(self.source) == 4
def test_iter(self): def test_iter(self):
l = [x for x in self.source] values = [x for x in self.source]
assert len(l) == 4 assert len(values) == 4
class TestSourceParsingAndCompiling(object): class TestSourceParsingAndCompiling(object):
@ -331,8 +331,8 @@ def test_getstartingblock_singleline():
x = A('x', 'y') x = A('x', 'y')
l = [i for i in x.source.lines if i.strip()] values = [i for i in x.source.lines if i.strip()]
assert len(l) == 1 assert len(values) == 1
def test_getline_finally(): def test_getline_finally():

View File

@ -22,5 +22,5 @@ def test_getstartingblock_multiline():
, ,
'z') 'z')
l = [i for i in x.source.lines if i.strip()] values = [i for i in x.source.lines if i.strip()]
assert len(l) == 4 assert len(values) == 4

View File

@ -873,11 +873,11 @@ class TestConftestCustomization(object):
def test_makeitem_non_underscore(self, testdir, monkeypatch): def test_makeitem_non_underscore(self, testdir, monkeypatch):
modcol = testdir.getmodulecol("def _hello(): pass") modcol = testdir.getmodulecol("def _hello(): pass")
l = [] values = []
monkeypatch.setattr(pytest.Module, 'makeitem', monkeypatch.setattr(pytest.Module, 'makeitem',
lambda self, name, obj: l.append(name)) lambda self, name, obj: values.append(name))
l = modcol.collect() values = modcol.collect()
assert '_hello' not in l assert '_hello' not in values
def test_issue2369_collect_module_fileext(self, testdir): def test_issue2369_collect_module_fileext(self, testdir):
"""Ensure we can collect files with weird file extensions as Python """Ensure we can collect files with weird file extensions as Python

View File

@ -548,12 +548,12 @@ class TestRequestBasic(object):
def test_getfixturevalue(self, testdir, getfixmethod): def test_getfixturevalue(self, testdir, getfixmethod):
item = testdir.getitem(""" item = testdir.getitem("""
import pytest import pytest
l = [2] values = [2]
@pytest.fixture @pytest.fixture
def something(request): return 1 def something(request): return 1
@pytest.fixture @pytest.fixture
def other(request): def other(request):
return l.pop() return values.pop()
def test_func(something): pass def test_func(something): pass
""") """)
import contextlib import contextlib
@ -622,15 +622,15 @@ class TestRequestBasic(object):
def test_request_addfinalizer_failing_setup(self, testdir): def test_request_addfinalizer_failing_setup(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [1] values = [1]
@pytest.fixture @pytest.fixture
def myfix(request): def myfix(request):
request.addfinalizer(l.pop) request.addfinalizer(values.pop)
assert 0 assert 0
def test_fix(myfix): def test_fix(myfix):
pass pass
def test_finalizer_ran(): def test_finalizer_ran():
assert not l assert not values
""") """)
reprec = testdir.inline_run("-s") reprec = testdir.inline_run("-s")
reprec.assertoutcome(failed=1, passed=1) reprec.assertoutcome(failed=1, passed=1)
@ -638,30 +638,30 @@ class TestRequestBasic(object):
def test_request_addfinalizer_failing_setup_module(self, testdir): def test_request_addfinalizer_failing_setup_module(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [1, 2] values = [1, 2]
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def myfix(request): def myfix(request):
request.addfinalizer(l.pop) request.addfinalizer(values.pop)
request.addfinalizer(l.pop) request.addfinalizer(values.pop)
assert 0 assert 0
def test_fix(myfix): def test_fix(myfix):
pass pass
""") """)
reprec = testdir.inline_run("-s") reprec = testdir.inline_run("-s")
mod = reprec.getcalls("pytest_runtest_setup")[0].item.module mod = reprec.getcalls("pytest_runtest_setup")[0].item.module
assert not mod.l assert not mod.values
def test_request_addfinalizer_partial_setup_failure(self, testdir): def test_request_addfinalizer_partial_setup_failure(self, testdir):
p = testdir.makepyfile(""" p = testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture @pytest.fixture
def something(request): def something(request):
request.addfinalizer(lambda: l.append(None)) request.addfinalizer(lambda: values.append(None))
def test_func(something, missingarg): def test_func(something, missingarg):
pass pass
def test_second(): def test_second():
assert len(l) == 1 assert len(values) == 1
""") """)
result = testdir.runpytest(p) result = testdir.runpytest(p)
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([
@ -675,7 +675,7 @@ class TestRequestBasic(object):
""" """
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
def _excepts(where): def _excepts(where):
raise Exception('Error in %s fixture' % where) raise Exception('Error in %s fixture' % where)
@pytest.fixture @pytest.fixture
@ -683,17 +683,17 @@ class TestRequestBasic(object):
return request return request
@pytest.fixture @pytest.fixture
def something(subrequest): def something(subrequest):
subrequest.addfinalizer(lambda: l.append(1)) subrequest.addfinalizer(lambda: values.append(1))
subrequest.addfinalizer(lambda: l.append(2)) subrequest.addfinalizer(lambda: values.append(2))
subrequest.addfinalizer(lambda: _excepts('something')) subrequest.addfinalizer(lambda: _excepts('something'))
@pytest.fixture @pytest.fixture
def excepts(subrequest): def excepts(subrequest):
subrequest.addfinalizer(lambda: _excepts('excepts')) subrequest.addfinalizer(lambda: _excepts('excepts'))
subrequest.addfinalizer(lambda: l.append(3)) subrequest.addfinalizer(lambda: values.append(3))
def test_first(something, excepts): def test_first(something, excepts):
pass pass
def test_second(): def test_second():
assert l == [3, 2, 1] assert values == [3, 2, 1]
""") """)
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([
@ -748,13 +748,13 @@ class TestRequestBasic(object):
def test_setupdecorator_and_xunit(self, testdir): def test_setupdecorator_and_xunit(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(scope='module', autouse=True) @pytest.fixture(scope='module', autouse=True)
def setup_module(): def setup_module():
l.append("module") values.append("module")
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def setup_function(): def setup_function():
l.append("function") values.append("function")
def test_func(): def test_func():
pass pass
@ -762,14 +762,14 @@ class TestRequestBasic(object):
class TestClass(object): class TestClass(object):
@pytest.fixture(scope="class", autouse=True) @pytest.fixture(scope="class", autouse=True)
def setup_class(self): def setup_class(self):
l.append("class") values.append("class")
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def setup_method(self): def setup_method(self):
l.append("method") values.append("method")
def test_method(self): def test_method(self):
pass pass
def test_all(): def test_all():
assert l == ["module", "function", "class", assert values == ["module", "function", "class",
"function", "method", "function"] "function", "method", "function"]
""") """)
reprec = testdir.inline_run("-v") reprec = testdir.inline_run("-v")
@ -930,10 +930,10 @@ class TestRequestCachedSetup(object):
def test_request_cachedsetup_extrakey(self, testdir): def test_request_cachedsetup_extrakey(self, testdir):
item1 = testdir.getitem("def test_func(): pass") item1 = testdir.getitem("def test_func(): pass")
req1 = fixtures.FixtureRequest(item1) req1 = fixtures.FixtureRequest(item1)
l = ["hello", "world"] values = ["hello", "world"]
def setup(): def setup():
return l.pop() return values.pop()
ret1 = req1.cached_setup(setup, extrakey=1) ret1 = req1.cached_setup(setup, extrakey=1)
ret2 = req1.cached_setup(setup, extrakey=2) ret2 = req1.cached_setup(setup, extrakey=2)
@ -947,24 +947,24 @@ class TestRequestCachedSetup(object):
def test_request_cachedsetup_cache_deletion(self, testdir): def test_request_cachedsetup_cache_deletion(self, testdir):
item1 = testdir.getitem("def test_func(): pass") item1 = testdir.getitem("def test_func(): pass")
req1 = fixtures.FixtureRequest(item1) req1 = fixtures.FixtureRequest(item1)
l = [] values = []
def setup(): def setup():
l.append("setup") values.append("setup")
def teardown(val): def teardown(val):
l.append("teardown") values.append("teardown")
req1.cached_setup(setup, teardown, scope="function") req1.cached_setup(setup, teardown, scope="function")
assert l == ['setup'] assert values == ['setup']
# artificial call of finalizer # artificial call of finalizer
setupstate = req1._pyfuncitem.session._setupstate setupstate = req1._pyfuncitem.session._setupstate
setupstate._callfinalizers(item1) setupstate._callfinalizers(item1)
assert l == ["setup", "teardown"] assert values == ["setup", "teardown"]
req1.cached_setup(setup, teardown, scope="function") req1.cached_setup(setup, teardown, scope="function")
assert l == ["setup", "teardown", "setup"] assert values == ["setup", "teardown", "setup"]
setupstate._callfinalizers(item1) setupstate._callfinalizers(item1)
assert l == ["setup", "teardown", "setup", "teardown"] assert values == ["setup", "teardown", "setup", "teardown"]
def test_request_cached_setup_two_args(self, testdir): def test_request_cached_setup_two_args(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
@ -1006,17 +1006,17 @@ class TestRequestCachedSetup(object):
def test_request_cached_setup_functional(self, testdir): def test_request_cached_setup_functional(self, testdir):
testdir.makepyfile(test_0=""" testdir.makepyfile(test_0="""
import pytest import pytest
l = [] values = []
@pytest.fixture @pytest.fixture
def something(request): def something(request):
val = request.cached_setup(fsetup, fteardown) val = request.cached_setup(fsetup, fteardown)
return val return val
def fsetup(mycache=[1]): def fsetup(mycache=[1]):
l.append(mycache.pop()) values.append(mycache.pop())
return l return values
def fteardown(something): def fteardown(something):
l.remove(something[0]) values.remove(something[0])
l.append(2) values.append(2)
def test_list_once(something): def test_list_once(something):
assert something == [1] assert something == [1]
def test_list_twice(something): def test_list_twice(something):
@ -1025,7 +1025,7 @@ class TestRequestCachedSetup(object):
testdir.makepyfile(test_1=""" testdir.makepyfile(test_1="""
import test_0 # should have run already import test_0 # should have run already
def test_check_test0_has_teardown_correct(): def test_check_test0_has_teardown_correct():
assert test_0.l == [2] assert test_0.values == [2]
""") """)
result = testdir.runpytest("-v") result = testdir.runpytest("-v")
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([
@ -1150,10 +1150,10 @@ class TestFixtureUsages(object):
def test_funcarg_parametrized_and_used_twice(self, testdir): def test_funcarg_parametrized_and_used_twice(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(params=[1,2]) @pytest.fixture(params=[1,2])
def arg1(request): def arg1(request):
l.append(1) values.append(1)
return request.param return request.param
@pytest.fixture() @pytest.fixture()
@ -1162,7 +1162,7 @@ class TestFixtureUsages(object):
def test_add(arg1, arg2): def test_add(arg1, arg2):
assert arg2 == arg1 + 1 assert arg2 == arg1 + 1
assert len(l) == arg1 assert len(values) == arg1
""") """)
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([
@ -1203,8 +1203,8 @@ class TestFixtureUsages(object):
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
l = reprec.getfailedcollections() values = reprec.getfailedcollections()
assert len(l) == 1 assert len(values) == 1
def test_request_can_be_overridden(self, testdir): def test_request_can_be_overridden(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
@ -1223,20 +1223,20 @@ class TestFixtureUsages(object):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(scope="class") @pytest.fixture(scope="class")
def myfix(request): def myfix(request):
request.cls.hello = "world" request.cls.hello = "world"
l.append(1) values.append(1)
class TestClass(object): class TestClass(object):
def test_one(self): def test_one(self):
assert self.hello == "world" assert self.hello == "world"
assert len(l) == 1 assert len(values) == 1
def test_two(self): def test_two(self):
assert self.hello == "world" assert self.hello == "world"
assert len(l) == 1 assert len(values) == 1
pytest.mark.usefixtures("myfix")(TestClass) pytest.mark.usefixtures("myfix")(TestClass)
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
@ -1290,7 +1290,7 @@ class TestFixtureUsages(object):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
def f(): def f():
yield 1 yield 1
yield 2 yield 2
@ -1304,14 +1304,14 @@ class TestFixtureUsages(object):
return request.param return request.param
def test_1(arg): def test_1(arg):
l.append(arg) values.append(arg)
def test_2(arg2): def test_2(arg2):
l.append(arg2*10) values.append(arg2*10)
""") """)
reprec = testdir.inline_run("-v") reprec = testdir.inline_run("-v")
reprec.assertoutcome(passed=4) reprec.assertoutcome(passed=4)
l = reprec.getcalls("pytest_runtest_call")[0].item.module.l values = reprec.getcalls("pytest_runtest_call")[0].item.module.values
assert l == [1, 2, 10, 20] assert values == [1, 2, 10, 20]
class TestFixtureManagerParseFactories(object): class TestFixtureManagerParseFactories(object):
@ -1461,19 +1461,19 @@ class TestAutouseDiscovery(object):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
class TestA(object): class TestA(object):
l = [] values = []
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def setup1(self): def setup1(self):
self.l.append(1) self.values.append(1)
def test_setup1(self): def test_setup1(self):
assert self.l == [1] assert self.values == [1]
class TestB(object): class TestB(object):
l = [] values = []
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def setup2(self): def setup2(self):
self.l.append(1) self.values.append(1)
def test_setup2(self): def test_setup2(self):
assert self.l == [1] assert self.values == [1]
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=2) reprec.assertoutcome(passed=2)
@ -1556,22 +1556,22 @@ class TestAutouseDiscovery(object):
def test_autouse_in_module_and_two_classes(self, testdir): def test_autouse_in_module_and_two_classes(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def append1(): def append1():
l.append("module") values.append("module")
def test_x(): def test_x():
assert l == ["module"] assert values == ["module"]
class TestA(object): class TestA(object):
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def append2(self): def append2(self):
l.append("A") values.append("A")
def test_hello(self): def test_hello(self):
assert l == ["module", "module", "A"], l assert values == ["module", "module", "A"], values
class TestA2(object): class TestA2(object):
def test_world(self): def test_world(self):
assert l == ["module", "module", "A", "module"], l assert values == ["module", "module", "A", "module"], values
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=3) reprec.assertoutcome(passed=3)
@ -1615,23 +1615,23 @@ class TestAutouseManagement(object):
def test_funcarg_and_setup(self, testdir): def test_funcarg_and_setup(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def arg(): def arg():
l.append(1) values.append(1)
return 0 return 0
@pytest.fixture(scope="module", autouse=True) @pytest.fixture(scope="module", autouse=True)
def something(arg): def something(arg):
l.append(2) values.append(2)
def test_hello(arg): def test_hello(arg):
assert len(l) == 2 assert len(values) == 2
assert l == [1,2] assert values == [1,2]
assert arg == 0 assert arg == 0
def test_hello2(arg): def test_hello2(arg):
assert len(l) == 2 assert len(values) == 2
assert l == [1,2] assert values == [1,2]
assert arg == 0 assert arg == 0
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
@ -1640,20 +1640,20 @@ class TestAutouseManagement(object):
def test_uses_parametrized_resource(self, testdir): def test_uses_parametrized_resource(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(params=[1,2]) @pytest.fixture(params=[1,2])
def arg(request): def arg(request):
return request.param return request.param
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def something(arg): def something(arg):
l.append(arg) values.append(arg)
def test_hello(): def test_hello():
if len(l) == 1: if len(values) == 1:
assert l == [1] assert values == [1]
elif len(l) == 2: elif len(values) == 2:
assert l == [1, 2] assert values == [1, 2]
else: else:
0/0 0/0
@ -1665,7 +1665,7 @@ class TestAutouseManagement(object):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(scope="session", params=[1,2]) @pytest.fixture(scope="session", params=[1,2])
def arg(request): def arg(request):
@ -1674,14 +1674,14 @@ class TestAutouseManagement(object):
@pytest.fixture(scope="function", autouse=True) @pytest.fixture(scope="function", autouse=True)
def append(request, arg): def append(request, arg):
if request.function.__name__ == "test_some": if request.function.__name__ == "test_some":
l.append(arg) values.append(arg)
def test_some(): def test_some():
pass pass
def test_result(arg): def test_result(arg):
assert len(l) == arg assert len(values) == arg
assert l[:arg] == [1,2][:arg] assert values[:arg] == [1,2][:arg]
""") """)
reprec = testdir.inline_run("-v", "-s") reprec = testdir.inline_run("-v", "-s")
reprec.assertoutcome(passed=4) reprec.assertoutcome(passed=4)
@ -1691,7 +1691,7 @@ class TestAutouseManagement(object):
import pytest import pytest
import pprint import pprint
l = [] values = []
@pytest.fixture(scope="function", params=[1,2]) @pytest.fixture(scope="function", params=[1,2])
def farg(request): def farg(request):
@ -1704,7 +1704,7 @@ class TestAutouseManagement(object):
@pytest.fixture(scope="function", autouse=True) @pytest.fixture(scope="function", autouse=True)
def append(request, farg, carg): def append(request, farg, carg):
def fin(): def fin():
l.append("fin_%s%s" % (carg, farg)) values.append("fin_%s%s" % (carg, farg))
request.addfinalizer(fin) request.addfinalizer(fin)
""") """)
testdir.makepyfile(""" testdir.makepyfile("""
@ -1721,26 +1721,26 @@ class TestAutouseManagement(object):
reprec = testdir.inline_run("-v", "-s", confcut) reprec = testdir.inline_run("-v", "-s", confcut)
reprec.assertoutcome(passed=8) reprec.assertoutcome(passed=8)
config = reprec.getcalls("pytest_unconfigure")[0].config config = reprec.getcalls("pytest_unconfigure")[0].config
l = config.pluginmanager._getconftestmodules(p)[0].l values = config.pluginmanager._getconftestmodules(p)[0].values
assert l == ["fin_a1", "fin_a2", "fin_b1", "fin_b2"] * 2 assert values == ["fin_a1", "fin_a2", "fin_b1", "fin_b2"] * 2
def test_scope_ordering(self, testdir): def test_scope_ordering(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(scope="function", autouse=True) @pytest.fixture(scope="function", autouse=True)
def fappend2(): def fappend2():
l.append(2) values.append(2)
@pytest.fixture(scope="class", autouse=True) @pytest.fixture(scope="class", autouse=True)
def classappend3(): def classappend3():
l.append(3) values.append(3)
@pytest.fixture(scope="module", autouse=True) @pytest.fixture(scope="module", autouse=True)
def mappend(): def mappend():
l.append(1) values.append(1)
class TestHallo(object): class TestHallo(object):
def test_method(self): def test_method(self):
assert l == [1,3,2] assert values == [1,3,2]
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=1) reprec.assertoutcome(passed=1)
@ -1748,23 +1748,23 @@ class TestAutouseManagement(object):
def test_parametrization_setup_teardown_ordering(self, testdir): def test_parametrization_setup_teardown_ordering(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
def pytest_generate_tests(metafunc): def pytest_generate_tests(metafunc):
if metafunc.cls is not None: if metafunc.cls is not None:
metafunc.parametrize("item", [1,2], scope="class") metafunc.parametrize("item", [1,2], scope="class")
class TestClass(object): class TestClass(object):
@pytest.fixture(scope="class", autouse=True) @pytest.fixture(scope="class", autouse=True)
def addteardown(self, item, request): def addteardown(self, item, request):
l.append("setup-%d" % item) values.append("setup-%d" % item)
request.addfinalizer(lambda: l.append("teardown-%d" % item)) request.addfinalizer(lambda: values.append("teardown-%d" % item))
def test_step1(self, item): def test_step1(self, item):
l.append("step1-%d" % item) values.append("step1-%d" % item)
def test_step2(self, item): def test_step2(self, item):
l.append("step2-%d" % item) values.append("step2-%d" % item)
def test_finish(): def test_finish():
print (l) print (values)
assert l == ["setup-1", "step1-1", "step2-1", "teardown-1", assert values == ["setup-1", "step1-1", "step2-1", "teardown-1",
"setup-2", "step1-2", "step2-2", "teardown-2",] "setup-2", "step1-2", "step2-2", "teardown-2",]
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
@ -1774,15 +1774,15 @@ class TestAutouseManagement(object):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def fix1(): def fix1():
l.append(1) values.append(1)
@pytest.fixture() @pytest.fixture()
def arg1(): def arg1():
l.append(2) values.append(2)
def test_hello(arg1): def test_hello(arg1):
assert l == [1,2] assert values == [1,2]
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=1) reprec.assertoutcome(passed=1)
@ -1793,20 +1793,20 @@ class TestAutouseManagement(object):
def test_ordering_dependencies_torndown_first(self, testdir, param1, param2): def test_ordering_dependencies_torndown_first(self, testdir, param1, param2):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(%(param1)s) @pytest.fixture(%(param1)s)
def arg1(request): def arg1(request):
request.addfinalizer(lambda: l.append("fin1")) request.addfinalizer(lambda: values.append("fin1"))
l.append("new1") values.append("new1")
@pytest.fixture(%(param2)s) @pytest.fixture(%(param2)s)
def arg2(request, arg1): def arg2(request, arg1):
request.addfinalizer(lambda: l.append("fin2")) request.addfinalizer(lambda: values.append("fin2"))
l.append("new2") values.append("new2")
def test_arg(arg2): def test_arg(arg2):
pass pass
def test_check(): def test_check():
assert l == ["new1", "new2", "fin2", "fin1"] assert values == ["new1", "new2", "fin2", "fin1"]
""" % locals()) """ % locals())
reprec = testdir.inline_run("-s") reprec = testdir.inline_run("-s")
reprec.assertoutcome(passed=2) reprec.assertoutcome(passed=2)
@ -1819,11 +1819,11 @@ class TestFixtureMarker(object):
@pytest.fixture(params=["a", "b", "c"]) @pytest.fixture(params=["a", "b", "c"])
def arg(request): def arg(request):
return request.param return request.param
l = [] values = []
def test_param(arg): def test_param(arg):
l.append(arg) values.append(arg)
def test_result(): def test_result():
assert l == list("abc") assert values == list("abc")
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=4) reprec.assertoutcome(passed=4)
@ -1867,21 +1867,21 @@ class TestFixtureMarker(object):
def test_scope_session(self, testdir): def test_scope_session(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def arg(): def arg():
l.append(1) values.append(1)
return 1 return 1
def test_1(arg): def test_1(arg):
assert arg == 1 assert arg == 1
def test_2(arg): def test_2(arg):
assert arg == 1 assert arg == 1
assert len(l) == 1 assert len(values) == 1
class TestClass(object): class TestClass(object):
def test3(self, arg): def test3(self, arg):
assert arg == 1 assert arg == 1
assert len(l) == 1 assert len(values) == 1
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=3) reprec.assertoutcome(passed=3)
@ -1889,10 +1889,10 @@ class TestFixtureMarker(object):
def test_scope_session_exc(self, testdir): def test_scope_session_exc(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def fix(): def fix():
l.append(1) values.append(1)
pytest.skip('skipping') pytest.skip('skipping')
def test_1(fix): def test_1(fix):
@ -1900,7 +1900,7 @@ class TestFixtureMarker(object):
def test_2(fix): def test_2(fix):
pass pass
def test_last(): def test_last():
assert l == [1] assert values == [1]
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(skipped=2, passed=1) reprec.assertoutcome(skipped=2, passed=1)
@ -1908,11 +1908,11 @@ class TestFixtureMarker(object):
def test_scope_session_exc_two_fix(self, testdir): def test_scope_session_exc_two_fix(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
m = [] m = []
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def a(): def a():
l.append(1) values.append(1)
pytest.skip('skipping') pytest.skip('skipping')
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def b(a): def b(a):
@ -1923,7 +1923,7 @@ class TestFixtureMarker(object):
def test_2(b): def test_2(b):
pass pass
def test_last(): def test_last():
assert l == [1] assert values == [1]
assert m == [] assert m == []
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
@ -1961,21 +1961,21 @@ class TestFixtureMarker(object):
def test_scope_module_uses_session(self, testdir): def test_scope_module_uses_session(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def arg(): def arg():
l.append(1) values.append(1)
return 1 return 1
def test_1(arg): def test_1(arg):
assert arg == 1 assert arg == 1
def test_2(arg): def test_2(arg):
assert arg == 1 assert arg == 1
assert len(l) == 1 assert len(values) == 1
class TestClass(object): class TestClass(object):
def test3(self, arg): def test3(self, arg):
assert arg == 1 assert arg == 1
assert len(l) == 1 assert len(values) == 1
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=3) reprec.assertoutcome(passed=3)
@ -2070,17 +2070,17 @@ class TestFixtureMarker(object):
@pytest.fixture(scope="module", params=["a", "b", "c"]) @pytest.fixture(scope="module", params=["a", "b", "c"])
def arg(request): def arg(request):
return request.param return request.param
l = [] values = []
def test_param(arg): def test_param(arg):
l.append(arg) values.append(arg)
""") """)
reprec = testdir.inline_run("-v") reprec = testdir.inline_run("-v")
reprec.assertoutcome(passed=3) reprec.assertoutcome(passed=3)
l = reprec.getcalls("pytest_runtest_call")[0].item.module.l values = reprec.getcalls("pytest_runtest_call")[0].item.module.values
assert len(l) == 3 assert len(values) == 3
assert "a" in l assert "a" in values
assert "b" in l assert "b" in values
assert "c" in l assert "c" in values
def test_scope_mismatch(self, testdir): def test_scope_mismatch(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
@ -2111,16 +2111,16 @@ class TestFixtureMarker(object):
def arg(request): def arg(request):
return request.param return request.param
l = [] values = []
def test_1(arg): def test_1(arg):
l.append(arg) values.append(arg)
def test_2(arg): def test_2(arg):
l.append(arg) values.append(arg)
""") """)
reprec = testdir.inline_run("-v") reprec = testdir.inline_run("-v")
reprec.assertoutcome(passed=4) reprec.assertoutcome(passed=4)
l = reprec.getcalls("pytest_runtest_call")[0].item.module.l values = reprec.getcalls("pytest_runtest_call")[0].item.module.values
assert l == [1, 1, 2, 2] assert values == [1, 1, 2, 2]
def test_module_parametrized_ordering(self, testdir): def test_module_parametrized_ordering(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
@ -2172,7 +2172,7 @@ class TestFixtureMarker(object):
testdir.makeconftest(""" testdir.makeconftest("""
import pytest import pytest
l = [] values = []
@pytest.fixture(scope="function", params=[1,2]) @pytest.fixture(scope="function", params=[1,2])
def farg(request): def farg(request):
@ -2185,7 +2185,7 @@ class TestFixtureMarker(object):
@pytest.fixture(scope="function", autouse=True) @pytest.fixture(scope="function", autouse=True)
def append(request, farg, carg): def append(request, farg, carg):
def fin(): def fin():
l.append("fin_%s%s" % (carg, farg)) values.append("fin_%s%s" % (carg, farg))
request.addfinalizer(fin) request.addfinalizer(fin)
""") """)
testdir.makepyfile(""" testdir.makepyfile("""
@ -2223,30 +2223,30 @@ class TestFixtureMarker(object):
@pytest.fixture(scope="function", params=[1, 2]) @pytest.fixture(scope="function", params=[1, 2])
def arg(request): def arg(request):
param = request.param param = request.param
request.addfinalizer(lambda: l.append("fin:%s" % param)) request.addfinalizer(lambda: values.append("fin:%s" % param))
l.append("create:%s" % param) values.append("create:%s" % param)
return request.param return request.param
@pytest.fixture(scope="module", params=["mod1", "mod2"]) @pytest.fixture(scope="module", params=["mod1", "mod2"])
def modarg(request): def modarg(request):
param = request.param param = request.param
request.addfinalizer(lambda: l.append("fin:%s" % param)) request.addfinalizer(lambda: values.append("fin:%s" % param))
l.append("create:%s" % param) values.append("create:%s" % param)
return request.param return request.param
l = [] values = []
def test_1(arg): def test_1(arg):
l.append("test1") values.append("test1")
def test_2(modarg): def test_2(modarg):
l.append("test2") values.append("test2")
def test_3(arg, modarg): def test_3(arg, modarg):
l.append("test3") values.append("test3")
def test_4(modarg, arg): def test_4(modarg, arg):
l.append("test4") values.append("test4")
""") """)
reprec = testdir.inline_run("-v") reprec = testdir.inline_run("-v")
reprec.assertoutcome(passed=12) reprec.assertoutcome(passed=12)
l = reprec.getcalls("pytest_runtest_call")[0].item.module.l values = reprec.getcalls("pytest_runtest_call")[0].item.module.values
expected = [ expected = [
'create:1', 'test1', 'fin:1', 'create:2', 'test1', 'create:1', 'test1', 'fin:1', 'create:2', 'test1',
'fin:2', 'create:mod1', 'test2', 'create:1', 'test3', 'fin:2', 'create:mod1', 'test2', 'create:1', 'test3',
@ -2257,8 +2257,8 @@ class TestFixtureMarker(object):
'test4', 'fin:1', 'create:2', 'test4', 'fin:2', 'test4', 'fin:1', 'create:2', 'test4', 'fin:2',
'fin:mod2'] 'fin:mod2']
import pprint import pprint
pprint.pprint(list(zip(l, expected))) pprint.pprint(list(zip(values, expected)))
assert l == expected assert values == expected
def test_parametrized_fixture_teardown_order(self, testdir): def test_parametrized_fixture_teardown_order(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
@ -2267,29 +2267,29 @@ class TestFixtureMarker(object):
def param1(request): def param1(request):
return request.param return request.param
l = [] values = []
class TestClass(object): class TestClass(object):
@classmethod @classmethod
@pytest.fixture(scope="class", autouse=True) @pytest.fixture(scope="class", autouse=True)
def setup1(self, request, param1): def setup1(self, request, param1):
l.append(1) values.append(1)
request.addfinalizer(self.teardown1) request.addfinalizer(self.teardown1)
@classmethod @classmethod
def teardown1(self): def teardown1(self):
assert l.pop() == 1 assert values.pop() == 1
@pytest.fixture(scope="class", autouse=True) @pytest.fixture(scope="class", autouse=True)
def setup2(self, request, param1): def setup2(self, request, param1):
l.append(2) values.append(2)
request.addfinalizer(self.teardown2) request.addfinalizer(self.teardown2)
@classmethod @classmethod
def teardown2(self): def teardown2(self):
assert l.pop() == 2 assert values.pop() == 2
def test(self): def test(self):
pass pass
def test_finish(): def test_finish():
assert not l assert not values
""") """)
result = testdir.runpytest("-v") result = testdir.runpytest("-v")
result.stdout.fnmatch_lines(""" result.stdout.fnmatch_lines("""
@ -2354,42 +2354,42 @@ class TestFixtureMarker(object):
def test_request_is_clean(self, testdir): def test_request_is_clean(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(params=[1, 2]) @pytest.fixture(params=[1, 2])
def fix(request): def fix(request):
request.addfinalizer(lambda: l.append(request.param)) request.addfinalizer(lambda: values.append(request.param))
def test_fix(fix): def test_fix(fix):
pass pass
""") """)
reprec = testdir.inline_run("-s") reprec = testdir.inline_run("-s")
l = reprec.getcalls("pytest_runtest_call")[0].item.module.l values = reprec.getcalls("pytest_runtest_call")[0].item.module.values
assert l == [1, 2] assert values == [1, 2]
def test_parametrize_separated_lifecycle(self, testdir): def test_parametrize_separated_lifecycle(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(scope="module", params=[1, 2]) @pytest.fixture(scope="module", params=[1, 2])
def arg(request): def arg(request):
x = request.param x = request.param
request.addfinalizer(lambda: l.append("fin%s" % x)) request.addfinalizer(lambda: values.append("fin%s" % x))
return request.param return request.param
def test_1(arg): def test_1(arg):
l.append(arg) values.append(arg)
def test_2(arg): def test_2(arg):
l.append(arg) values.append(arg)
""") """)
reprec = testdir.inline_run("-vs") reprec = testdir.inline_run("-vs")
reprec.assertoutcome(passed=4) reprec.assertoutcome(passed=4)
l = reprec.getcalls("pytest_runtest_call")[0].item.module.l values = reprec.getcalls("pytest_runtest_call")[0].item.module.values
import pprint import pprint
pprint.pprint(l) pprint.pprint(values)
# assert len(l) == 6 # assert len(values) == 6
assert l[0] == l[1] == 1 assert values[0] == values[1] == 1
assert l[2] == "fin1" assert values[2] == "fin1"
assert l[3] == l[4] == 2 assert values[3] == values[4] == 2
assert l[5] == "fin2" assert values[5] == "fin2"
def test_parametrize_function_scoped_finalizers_called(self, testdir): def test_parametrize_function_scoped_finalizers_called(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
@ -2398,17 +2398,17 @@ class TestFixtureMarker(object):
@pytest.fixture(scope="function", params=[1, 2]) @pytest.fixture(scope="function", params=[1, 2])
def arg(request): def arg(request):
x = request.param x = request.param
request.addfinalizer(lambda: l.append("fin%s" % x)) request.addfinalizer(lambda: values.append("fin%s" % x))
return request.param return request.param
l = [] values = []
def test_1(arg): def test_1(arg):
l.append(arg) values.append(arg)
def test_2(arg): def test_2(arg):
l.append(arg) values.append(arg)
def test_3(): def test_3():
assert len(l) == 8 assert len(values) == 8
assert l == [1, "fin1", 2, "fin2", 1, "fin1", 2, "fin2"] assert values == [1, "fin1", 2, "fin2", 1, "fin1", 2, "fin2"]
""") """)
reprec = testdir.inline_run("-v") reprec = testdir.inline_run("-v")
reprec.assertoutcome(passed=5) reprec.assertoutcome(passed=5)
@ -2418,7 +2418,7 @@ class TestFixtureMarker(object):
def test_finalizer_order_on_parametrization(self, scope, testdir): def test_finalizer_order_on_parametrization(self, scope, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(scope=%(scope)r, params=["1"]) @pytest.fixture(scope=%(scope)r, params=["1"])
def fix1(request): def fix1(request):
@ -2427,13 +2427,13 @@ class TestFixtureMarker(object):
@pytest.fixture(scope=%(scope)r) @pytest.fixture(scope=%(scope)r)
def fix2(request, base): def fix2(request, base):
def cleanup_fix2(): def cleanup_fix2():
assert not l, "base should not have been finalized" assert not values, "base should not have been finalized"
request.addfinalizer(cleanup_fix2) request.addfinalizer(cleanup_fix2)
@pytest.fixture(scope=%(scope)r) @pytest.fixture(scope=%(scope)r)
def base(request, fix1): def base(request, fix1):
def cleanup_base(): def cleanup_base():
l.append("fin_base") values.append("fin_base")
print ("finalizing base") print ("finalizing base")
request.addfinalizer(cleanup_base) request.addfinalizer(cleanup_base)
@ -2451,27 +2451,27 @@ class TestFixtureMarker(object):
def test_class_scope_parametrization_ordering(self, testdir): def test_class_scope_parametrization_ordering(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
@pytest.fixture(params=["John", "Doe"], scope="class") @pytest.fixture(params=["John", "Doe"], scope="class")
def human(request): def human(request):
request.addfinalizer(lambda: l.append("fin %s" % request.param)) request.addfinalizer(lambda: values.append("fin %s" % request.param))
return request.param return request.param
class TestGreetings(object): class TestGreetings(object):
def test_hello(self, human): def test_hello(self, human):
l.append("test_hello") values.append("test_hello")
class TestMetrics(object): class TestMetrics(object):
def test_name(self, human): def test_name(self, human):
l.append("test_name") values.append("test_name")
def test_population(self, human): def test_population(self, human):
l.append("test_population") values.append("test_population")
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=6) reprec.assertoutcome(passed=6)
l = reprec.getcalls("pytest_runtest_call")[0].item.module.l values = reprec.getcalls("pytest_runtest_call")[0].item.module.values
assert l == ["test_hello", "fin John", "test_hello", "fin Doe", assert values == ["test_hello", "fin John", "test_hello", "fin Doe",
"test_name", "test_population", "fin John", "test_name", "test_population", "fin John",
"test_name", "test_population", "fin Doe"] "test_name", "test_population", "fin Doe"]
@ -2485,21 +2485,21 @@ class TestFixtureMarker(object):
@pytest.fixture(scope="module", autouse=True) @pytest.fixture(scope="module", autouse=True)
def mysetup(request, arg): def mysetup(request, arg):
request.addfinalizer(lambda: l.append("fin%s" % arg)) request.addfinalizer(lambda: values.append("fin%s" % arg))
l.append("setup%s" % arg) values.append("setup%s" % arg)
l = [] values = []
def test_1(arg): def test_1(arg):
l.append(arg) values.append(arg)
def test_2(arg): def test_2(arg):
l.append(arg) values.append(arg)
def test_3(): def test_3():
import pprint import pprint
pprint.pprint(l) pprint.pprint(values)
if arg == 1: if arg == 1:
assert l == ["setup1", 1, 1, ] assert values == ["setup1", 1, 1, ]
elif arg == 2: elif arg == 2:
assert l == ["setup1", 1, 1, "fin1", assert values == ["setup1", 1, 1, "fin1",
"setup2", 2, 2, ] "setup2", 2, 2, ]
""") """)
@ -2660,13 +2660,13 @@ class TestErrors(object):
request.addfinalizer(f) request.addfinalizer(f)
return object() return object()
l = [] values = []
def test_1(fix1): def test_1(fix1):
l.append(fix1) values.append(fix1)
def test_2(fix1): def test_2(fix1):
l.append(fix1) values.append(fix1)
def test_3(): def test_3():
assert l[0] != l[1] assert values[0] != values[1]
""") """)
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines(""" result.stdout.fnmatch_lines("""

View File

@ -93,8 +93,8 @@ class TestMockDecoration(object):
def f(x): def f(x):
pass pass
l = getfuncargnames(f) values = getfuncargnames(f)
assert l == ("x",) assert values == ("x",)
def test_wrapped_getfuncargnames_patching(self): def test_wrapped_getfuncargnames_patching(self):
from _pytest.compat import getfuncargnames from _pytest.compat import getfuncargnames
@ -110,8 +110,8 @@ class TestMockDecoration(object):
def f(x, y, z): def f(x, y, z):
pass pass
l = getfuncargnames(f) values = getfuncargnames(f)
assert l == ("y", "z") assert values == ("y", "z")
def test_unittest_mock(self, testdir): def test_unittest_mock(self, testdir):
pytest.importorskip("unittest.mock") pytest.importorskip("unittest.mock")

View File

@ -1071,21 +1071,21 @@ class TestMetafuncFunctional(object):
def test_parametrize_scope_overrides(self, testdir, scope, length): def test_parametrize_scope_overrides(self, testdir, scope, length):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
l = [] values = []
def pytest_generate_tests(metafunc): def pytest_generate_tests(metafunc):
if "arg" in metafunc.funcargnames: if "arg" in metafunc.funcargnames:
metafunc.parametrize("arg", [1,2], indirect=True, metafunc.parametrize("arg", [1,2], indirect=True,
scope=%r) scope=%r)
@pytest.fixture @pytest.fixture
def arg(request): def arg(request):
l.append(request.param) values.append(request.param)
return request.param return request.param
def test_hello(arg): def test_hello(arg):
assert arg in (1,2) assert arg in (1,2)
def test_world(arg): def test_world(arg):
assert arg in (1,2) assert arg in (1,2)
def test_checklength(): def test_checklength():
assert len(l) == %d assert len(values) == %d
""" % (scope, length)) """ % (scope, length))
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=5) reprec.assertoutcome(passed=5)

View File

@ -135,3 +135,24 @@ def test_verbose_include_private_fixtures_and_loc(testdir):
'arg3 -- test_verbose_include_private_fixtures_and_loc.py:3', 'arg3 -- test_verbose_include_private_fixtures_and_loc.py:3',
' arg3 from testmodule', ' arg3 from testmodule',
]) ])
def test_doctest_items(testdir):
testdir.makepyfile('''
def foo():
"""
>>> 1 + 1
2
"""
''')
testdir.maketxtfile('''
>>> 1 + 1
2
''')
result = testdir.runpytest("--fixtures-per-test", "--doctest-modules",
"--doctest-glob=*.txt", "-v")
assert result.ret == 0
result.stdout.fnmatch_lines([
'*collected 2 items*',
])

View File

@ -82,7 +82,7 @@ class TestArgComplete(object):
from _pytest._argcomplete import FastFilesCompleter from _pytest._argcomplete import FastFilesCompleter
ffc = FastFilesCompleter() ffc = FastFilesCompleter()
fc = FilesCompleter() fc = FilesCompleter()
for x in '/ /d /data qqq'.split(): for x in ['/', '/d', '/data', 'qqq', '']:
assert equal_with_bash(x, ffc, fc, out=py.std.sys.stdout) assert equal_with_bash(x, ffc, fc, out=py.std.sys.stdout)
@pytest.mark.skipif("sys.platform in ('win32', 'darwin')") @pytest.mark.skipif("sys.platform in ('win32', 'darwin')")

View File

@ -229,9 +229,9 @@ class TestImportHookInstallation(object):
return pkg.helper.tool return pkg.helper.tool
""", """,
'pkg/other.py': """ 'pkg/other.py': """
l = [3, 2] values = [3, 2]
def tool(): def tool():
assert l.pop() == 3 assert values.pop() == 3
""", """,
'conftest.py': """ 'conftest.py': """
pytest_plugins = ['pkg.plugin'] pytest_plugins = ['pkg.plugin']
@ -248,7 +248,7 @@ class TestImportHookInstallation(object):
result = testdir.runpytest_subprocess('--assert=rewrite') result = testdir.runpytest_subprocess('--assert=rewrite')
result.stdout.fnmatch_lines(['>*assert a == b*', result.stdout.fnmatch_lines(['>*assert a == b*',
'E*assert 2 == 3*', 'E*assert 2 == 3*',
'>*assert l.pop() == 3*', '>*assert values.pop() == 3*',
'E*AssertionError']) 'E*AssertionError'])
def test_register_assert_rewrite_checks_types(self): def test_register_assert_rewrite_checks_types(self):
@ -263,13 +263,13 @@ class TestBinReprIntegration(object):
def test_pytest_assertrepr_compare_called(self, testdir): def test_pytest_assertrepr_compare_called(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
import pytest import pytest
l = [] values = []
def pytest_assertrepr_compare(op, left, right): def pytest_assertrepr_compare(op, left, right):
l.append((op, left, right)) values.append((op, left, right))
@pytest.fixture @pytest.fixture
def list(request): def list(request):
return l return values
""") """)
testdir.makepyfile(""" testdir.makepyfile("""
def test_hello(): def test_hello():

View File

@ -65,13 +65,18 @@ class TestAssertionRewrite(object):
def test_place_initial_imports(self): def test_place_initial_imports(self):
s = """'Doc string'\nother = stuff""" s = """'Doc string'\nother = stuff"""
m = rewrite(s) m = rewrite(s)
# Module docstrings in 3.7 are part of Module node, it's not in the body
# so we remove it so the following body items have the same indexes on
# all Python versions
if sys.version_info < (3, 7):
assert isinstance(m.body[0], ast.Expr) assert isinstance(m.body[0], ast.Expr)
assert isinstance(m.body[0].value, ast.Str) assert isinstance(m.body[0].value, ast.Str)
for imp in m.body[1:3]: del m.body[0]
for imp in m.body[0:2]:
assert isinstance(imp, ast.Import) assert isinstance(imp, ast.Import)
assert imp.lineno == 2 assert imp.lineno == 2
assert imp.col_offset == 0 assert imp.col_offset == 0
assert isinstance(m.body[3], ast.Assign) assert isinstance(m.body[2], ast.Assign)
s = """from __future__ import with_statement\nother_stuff""" s = """from __future__ import with_statement\nother_stuff"""
m = rewrite(s) m = rewrite(s)
assert isinstance(m.body[0], ast.ImportFrom) assert isinstance(m.body[0], ast.ImportFrom)
@ -80,16 +85,29 @@ class TestAssertionRewrite(object):
assert imp.lineno == 2 assert imp.lineno == 2
assert imp.col_offset == 0 assert imp.col_offset == 0
assert isinstance(m.body[3], ast.Expr) assert isinstance(m.body[3], ast.Expr)
s = """'doc string'\nfrom __future__ import with_statement\nother""" s = """'doc string'\nfrom __future__ import with_statement"""
m = rewrite(s) m = rewrite(s)
if sys.version_info < (3, 7):
assert isinstance(m.body[0], ast.Expr) assert isinstance(m.body[0], ast.Expr)
assert isinstance(m.body[0].value, ast.Str) assert isinstance(m.body[0].value, ast.Str)
assert isinstance(m.body[1], ast.ImportFrom) del m.body[0]
for imp in m.body[2:4]: assert isinstance(m.body[0], ast.ImportFrom)
for imp in m.body[1:3]:
assert isinstance(imp, ast.Import)
assert imp.lineno == 2
assert imp.col_offset == 0
s = """'doc string'\nfrom __future__ import with_statement\nother"""
m = rewrite(s)
if sys.version_info < (3, 7):
assert isinstance(m.body[0], ast.Expr)
assert isinstance(m.body[0].value, ast.Str)
del m.body[0]
assert isinstance(m.body[0], ast.ImportFrom)
for imp in m.body[1:3]:
assert isinstance(imp, ast.Import) assert isinstance(imp, ast.Import)
assert imp.lineno == 3 assert imp.lineno == 3
assert imp.col_offset == 0 assert imp.col_offset == 0
assert isinstance(m.body[4], ast.Expr) assert isinstance(m.body[3], ast.Expr)
s = """from . import relative\nother_stuff""" s = """from . import relative\nother_stuff"""
m = rewrite(s) m = rewrite(s)
for imp in m.body[0:2]: for imp in m.body[0:2]:
@ -101,10 +119,14 @@ class TestAssertionRewrite(object):
def test_dont_rewrite(self): def test_dont_rewrite(self):
s = """'PYTEST_DONT_REWRITE'\nassert 14""" s = """'PYTEST_DONT_REWRITE'\nassert 14"""
m = rewrite(s) m = rewrite(s)
if sys.version_info < (3, 7):
assert len(m.body) == 2 assert len(m.body) == 2
assert isinstance(m.body[0], ast.Expr)
assert isinstance(m.body[0].value, ast.Str) assert isinstance(m.body[0].value, ast.Str)
assert isinstance(m.body[1], ast.Assert) del m.body[0]
assert m.body[1].msg is None else:
assert len(m.body) == 1
assert m.body[0].msg is None
def test_name(self): def test_name(self):
def f(): def f():
@ -451,8 +473,8 @@ class TestAssertionRewrite(object):
def test_len(self): def test_len(self):
def f(): def f():
l = list(range(10)) values = list(range(10))
assert len(l) == 11 assert len(values) == 11
assert getmsg(f).startswith("""assert 10 == 11 assert getmsg(f).startswith("""assert 10 == 11
+ where 10 = len([""") + where 10 = len([""")

View File

@ -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
@ -702,9 +703,9 @@ class TestNodekeywords(object):
def test_pass(): pass def test_pass(): pass
def test_fail(): assert 0 def test_fail(): assert 0
""") """)
l = list(modcol.keywords) values = list(modcol.keywords)
assert modcol.name in l assert modcol.name in values
for x in l: for x in values:
assert not x.startswith("_") assert not x.startswith("_")
assert modcol.name in repr(modcol.keywords) assert modcol.name in repr(modcol.keywords)
@ -830,3 +831,28 @@ def test_continue_on_collection_errors_maxfail(testdir):
"*Interrupted: stopping after 3 failures*", "*Interrupted: stopping after 3 failures*",
"*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*",
])

View File

@ -123,11 +123,11 @@ class TestConfigCmdlineParsing(object):
class TestConfigAPI(object): class TestConfigAPI(object):
def test_config_trace(self, testdir): def test_config_trace(self, testdir):
config = testdir.parseconfig() config = testdir.parseconfig()
l = [] values = []
config.trace.root.setwriter(l.append) config.trace.root.setwriter(values.append)
config.trace("hello") config.trace("hello")
assert len(l) == 1 assert len(values) == 1
assert l[0] == "hello [config]\n" assert values[0] == "hello [config]\n"
def test_config_getoption(self, testdir): def test_config_getoption(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
@ -209,10 +209,10 @@ class TestConfigAPI(object):
paths=hello world/sub.py paths=hello world/sub.py
""") """)
config = testdir.parseconfig() config = testdir.parseconfig()
l = config.getini("paths") values = config.getini("paths")
assert len(l) == 2 assert len(values) == 2
assert l[0] == p.dirpath('hello') assert values[0] == p.dirpath('hello')
assert l[1] == p.dirpath('world/sub.py') assert values[1] == p.dirpath('world/sub.py')
pytest.raises(ValueError, config.getini, 'other') pytest.raises(ValueError, config.getini, 'other')
def test_addini_args(self, testdir): def test_addini_args(self, testdir):
@ -226,11 +226,11 @@ class TestConfigAPI(object):
args=123 "123 hello" "this" args=123 "123 hello" "this"
""") """)
config = testdir.parseconfig() config = testdir.parseconfig()
l = config.getini("args") values = config.getini("args")
assert len(l) == 3 assert len(values) == 3
assert l == ["123", "123 hello", "this"] assert values == ["123", "123 hello", "this"]
l = config.getini("a2") values = config.getini("a2")
assert l == list("123") assert values == list("123")
def test_addini_linelist(self, testdir): def test_addini_linelist(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
@ -244,11 +244,11 @@ class TestConfigAPI(object):
second line second line
""") """)
config = testdir.parseconfig() config = testdir.parseconfig()
l = config.getini("xy") values = config.getini("xy")
assert len(l) == 2 assert len(values) == 2
assert l == ["123 345", "second line"] assert values == ["123 345", "second line"]
l = config.getini("a2") values = config.getini("a2")
assert l == [] assert values == []
@pytest.mark.parametrize('str_val, bool_val', @pytest.mark.parametrize('str_val, bool_val',
[('True', True), ('no', False), ('no-ini', True)]) [('True', True), ('no', False), ('no-ini', True)])
@ -275,13 +275,13 @@ class TestConfigAPI(object):
xy= 123 xy= 123
""") """)
config = testdir.parseconfig() config = testdir.parseconfig()
l = config.getini("xy") values = config.getini("xy")
assert len(l) == 1 assert len(values) == 1
assert l == ["123"] assert values == ["123"]
config.addinivalue_line("xy", "456") config.addinivalue_line("xy", "456")
l = config.getini("xy") values = config.getini("xy")
assert len(l) == 2 assert len(values) == 2
assert l == ["123", "456"] assert values == ["123", "456"]
def test_addinivalue_line_new(self, testdir): def test_addinivalue_line_new(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
@ -291,13 +291,13 @@ class TestConfigAPI(object):
config = testdir.parseconfig() config = testdir.parseconfig()
assert not config.getini("xy") assert not config.getini("xy")
config.addinivalue_line("xy", "456") config.addinivalue_line("xy", "456")
l = config.getini("xy") values = config.getini("xy")
assert len(l) == 1 assert len(values) == 1
assert l == ["456"] assert values == ["456"]
config.addinivalue_line("xy", "123") config.addinivalue_line("xy", "123")
l = config.getini("xy") values = config.getini("xy")
assert len(l) == 2 assert len(values) == 2
assert l == ["456", "123"] assert values == ["456", "123"]
def test_confcutdir_check_isdir(self, testdir): def test_confcutdir_check_isdir(self, testdir):
"""Give an error if --confcutdir is not a valid directory (#2078)""" """Give an error if --confcutdir is not a valid directory (#2078)"""
@ -596,13 +596,13 @@ def test_load_initial_conftest_last_ordering(testdir):
m = My() m = My()
pm.register(m) pm.register(m)
hc = pm.hook.pytest_load_initial_conftests hc = pm.hook.pytest_load_initial_conftests
l = hc._nonwrappers + hc._wrappers values = hc._nonwrappers + hc._wrappers
expected = [ expected = [
"_pytest.config", "_pytest.config",
'test_config', 'test_config',
'_pytest.capture', '_pytest.capture',
] ]
assert [x.function.__module__ for x in l] == expected assert [x.function.__module__ for x in values] == expected
def test_get_plugin_specs_as_list(): def test_get_plugin_specs_as_list():
@ -623,17 +623,17 @@ def test_get_plugin_specs_as_list():
class TestWarning(object): class TestWarning(object):
def test_warn_config(self, testdir): def test_warn_config(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
l = [] values = []
def pytest_configure(config): def pytest_configure(config):
config.warn("C1", "hello") config.warn("C1", "hello")
def pytest_logwarning(code, message): def pytest_logwarning(code, message):
if message == "hello" and code == "C1": if message == "hello" and code == "C1":
l.append(1) values.append(1)
""") """)
testdir.makepyfile(""" testdir.makepyfile("""
def test_proper(pytestconfig): def test_proper(pytestconfig):
import conftest import conftest
assert conftest.l == [1] assert conftest.values == [1]
""") """)
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=1) reprec.assertoutcome(passed=1)

View File

@ -87,8 +87,8 @@ def test_doubledash_considered(testdir):
conf.join("conftest.py").ensure() conf.join("conftest.py").ensure()
conftest = PytestPluginManager() conftest = PytestPluginManager()
conftest_setinitial(conftest, [conf.basename, conf.basename]) conftest_setinitial(conftest, [conf.basename, conf.basename])
l = conftest._getconftestmodules(conf) values = conftest._getconftestmodules(conf)
assert len(l) == 1 assert len(values) == 1
def test_issue151_load_all_conftests(testdir): def test_issue151_load_all_conftests(testdir):
@ -130,28 +130,28 @@ def test_conftestcutdir(testdir):
p = testdir.mkdir("x") p = testdir.mkdir("x")
conftest = PytestPluginManager() conftest = PytestPluginManager()
conftest_setinitial(conftest, [testdir.tmpdir], confcutdir=p) conftest_setinitial(conftest, [testdir.tmpdir], confcutdir=p)
l = conftest._getconftestmodules(p) values = conftest._getconftestmodules(p)
assert len(l) == 0 assert len(values) == 0
l = conftest._getconftestmodules(conf.dirpath()) values = conftest._getconftestmodules(conf.dirpath())
assert len(l) == 0 assert len(values) == 0
assert conf not in conftest._conftestpath2mod assert conf not in conftest._conftestpath2mod
# but we can still import a conftest directly # but we can still import a conftest directly
conftest._importconftest(conf) conftest._importconftest(conf)
l = conftest._getconftestmodules(conf.dirpath()) values = conftest._getconftestmodules(conf.dirpath())
assert l[0].__file__.startswith(str(conf)) assert values[0].__file__.startswith(str(conf))
# and all sub paths get updated properly # and all sub paths get updated properly
l = conftest._getconftestmodules(p) values = conftest._getconftestmodules(p)
assert len(l) == 1 assert len(values) == 1
assert l[0].__file__.startswith(str(conf)) assert values[0].__file__.startswith(str(conf))
def test_conftestcutdir_inplace_considered(testdir): def test_conftestcutdir_inplace_considered(testdir):
conf = testdir.makeconftest("") conf = testdir.makeconftest("")
conftest = PytestPluginManager() conftest = PytestPluginManager()
conftest_setinitial(conftest, [conf.dirpath()], confcutdir=conf.dirpath()) conftest_setinitial(conftest, [conf.dirpath()], confcutdir=conf.dirpath())
l = conftest._getconftestmodules(conf.dirpath()) values = conftest._getconftestmodules(conf.dirpath())
assert len(l) == 1 assert len(values) == 1
assert l[0].__file__.startswith(str(conf)) assert values[0].__file__.startswith(str(conf))
@pytest.mark.parametrize("name", 'test tests whatever .dotdir'.split()) @pytest.mark.parametrize("name", 'test tests whatever .dotdir'.split())

View File

@ -173,7 +173,7 @@ class TestDoctests(object):
"*UNEXPECTED*ZeroDivision*", "*UNEXPECTED*ZeroDivision*",
]) ])
def test_docstring_context_around_error(self, testdir): def test_docstring_partial_context_around_error(self, testdir):
"""Test that we show some context before the actual line of a failing """Test that we show some context before the actual line of a failing
doctest. doctest.
""" """
@ -199,7 +199,7 @@ class TestDoctests(object):
''') ''')
result = testdir.runpytest('--doctest-modules') result = testdir.runpytest('--doctest-modules')
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([
'*docstring_context_around_error*', '*docstring_partial_context_around_error*',
'005*text-line-3', '005*text-line-3',
'006*text-line-4', '006*text-line-4',
'013*text-line-11', '013*text-line-11',
@ -213,6 +213,32 @@ class TestDoctests(object):
assert 'text-line-2' not in result.stdout.str() assert 'text-line-2' not in result.stdout.str()
assert 'text-line-after' not in result.stdout.str() assert 'text-line-after' not in result.stdout.str()
def test_docstring_full_context_around_error(self, testdir):
"""Test that we show the whole context before the actual line of a failing
doctest, provided that the context is up to 10 lines long.
"""
testdir.makepyfile('''
def foo():
"""
text-line-1
text-line-2
>>> 1 + 1
3
"""
''')
result = testdir.runpytest('--doctest-modules')
result.stdout.fnmatch_lines([
'*docstring_full_context_around_error*',
'003*text-line-1',
'004*text-line-2',
'006*>>> 1 + 1',
'Expected:',
' 3',
'Got:',
' 2',
])
def test_doctest_linedata_missing(self, testdir): def test_doctest_linedata_missing(self, testdir):
testdir.tmpdir.join('hello.py').write(_pytest._code.Source(""" testdir.tmpdir.join('hello.py').write(_pytest._code.Source("""
class Fun(object): class Fun(object):

View File

@ -169,6 +169,23 @@ def test_markers_option(testdir):
]) ])
def test_ini_markers_whitespace(testdir):
testdir.makeini("""
[pytest]
markers =
a1 : this is a whitespace marker
""")
testdir.makepyfile("""
import pytest
@pytest.mark.a1
def test_markers():
assert True
""")
rec = testdir.inline_run("--strict", "-m", "a1")
rec.assertoutcome(passed=1)
def test_markers_option_with_plugin_in_current_dir(testdir): def test_markers_option_with_plugin_in_current_dir(testdir):
testdir.makeconftest('pytest_plugins = "flip_flop"') testdir.makeconftest('pytest_plugins = "flip_flop"')
testdir.makepyfile(flip_flop="""\ testdir.makepyfile(flip_flop="""\
@ -342,6 +359,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):
@ -433,11 +468,11 @@ class TestFunctional(object):
assert marker.kwargs == {'x': 1, 'y': 2, 'z': 4} assert marker.kwargs == {'x': 1, 'y': 2, 'z': 4}
# test the new __iter__ interface # test the new __iter__ interface
l = list(marker) values = list(marker)
assert len(l) == 3 assert len(values) == 3
assert l[0].args == ("pos0",) assert values[0].args == ("pos0",)
assert l[1].args == () assert values[1].args == ()
assert l[2].args == ("pos1", ) assert values[2].args == ("pos1", )
@pytest.mark.xfail(reason='unfixed') @pytest.mark.xfail(reason='unfixed')
def test_merging_markers_deep(self, testdir): def test_merging_markers_deep(self, testdir):
@ -529,9 +564,9 @@ class TestFunctional(object):
def test_func(): def test_func():
pass pass
""") """)
l = reprec.getfailedcollections() values = reprec.getfailedcollections()
assert len(l) == 1 assert len(values) == 1
assert "TypeError" in str(l[0].longrepr) assert "TypeError" in str(values[0].longrepr)
def test_mark_dynamically_in_funcarg(self, testdir): def test_mark_dynamically_in_funcarg(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
@ -540,8 +575,8 @@ class TestFunctional(object):
def arg(request): def arg(request):
request.applymarker(pytest.mark.hello) request.applymarker(pytest.mark.hello)
def pytest_terminal_summary(terminalreporter): def pytest_terminal_summary(terminalreporter):
l = terminalreporter.stats['passed'] values = terminalreporter.stats['passed']
terminalreporter.writer.line("keyword: %s" % l[0].keywords) terminalreporter.writer.line("keyword: %s" % values[0].keywords)
""") """)
testdir.makepyfile(""" testdir.makepyfile("""
def test_func(arg): def test_func(arg):
@ -564,10 +599,10 @@ class TestFunctional(object):
item, = items item, = items
keywords = item.keywords keywords = item.keywords
marker = keywords['hello'] marker = keywords['hello']
l = list(marker) values = list(marker)
assert len(l) == 2 assert len(values) == 2
assert l[0].args == ("pos0",) assert values[0].args == ("pos0",)
assert l[1].args == ("pos1",) assert values[1].args == ("pos1",)
def test_no_marker_match_on_unmarked_names(self, testdir): def test_no_marker_match_on_unmarked_names(self, testdir):
p = testdir.makepyfile(""" p = testdir.makepyfile("""

18
testing/test_nodes.py Normal file
View File

@ -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

View File

@ -8,18 +8,18 @@ def setup_module(mod):
def test_nose_setup(testdir): def test_nose_setup(testdir):
p = testdir.makepyfile(""" p = testdir.makepyfile("""
l = [] values = []
from nose.tools import with_setup from nose.tools import with_setup
@with_setup(lambda: l.append(1), lambda: l.append(2)) @with_setup(lambda: values.append(1), lambda: values.append(2))
def test_hello(): def test_hello():
assert l == [1] assert values == [1]
def test_world(): def test_world():
assert l == [1,2] assert values == [1,2]
test_hello.setup = lambda: l.append(1) test_hello.setup = lambda: values.append(1)
test_hello.teardown = lambda: l.append(2) test_hello.teardown = lambda: values.append(2)
""") """)
result = testdir.runpytest(p, '-p', 'nose') result = testdir.runpytest(p, '-p', 'nose')
result.assert_outcomes(passed=2) result.assert_outcomes(passed=2)
@ -27,15 +27,15 @@ def test_nose_setup(testdir):
def test_setup_func_with_setup_decorator(): def test_setup_func_with_setup_decorator():
from _pytest.nose import call_optional from _pytest.nose import call_optional
l = [] values = []
class A(object): class A(object):
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def f(self): def f(self):
l.append(1) values.append(1)
call_optional(A(), "f") call_optional(A(), "f")
assert not l assert not values
def test_setup_func_not_callable(): def test_setup_func_not_callable():
@ -51,24 +51,24 @@ def test_nose_setup_func(testdir):
p = testdir.makepyfile(""" p = testdir.makepyfile("""
from nose.tools import with_setup from nose.tools import with_setup
l = [] values = []
def my_setup(): def my_setup():
a = 1 a = 1
l.append(a) values.append(a)
def my_teardown(): def my_teardown():
b = 2 b = 2
l.append(b) values.append(b)
@with_setup(my_setup, my_teardown) @with_setup(my_setup, my_teardown)
def test_hello(): def test_hello():
print (l) print (values)
assert l == [1] assert values == [1]
def test_world(): def test_world():
print (l) print (values)
assert l == [1,2] assert values == [1,2]
""") """)
result = testdir.runpytest(p, '-p', 'nose') result = testdir.runpytest(p, '-p', 'nose')
@ -79,18 +79,18 @@ def test_nose_setup_func_failure(testdir):
p = testdir.makepyfile(""" p = testdir.makepyfile("""
from nose.tools import with_setup from nose.tools import with_setup
l = [] values = []
my_setup = lambda x: 1 my_setup = lambda x: 1
my_teardown = lambda x: 2 my_teardown = lambda x: 2
@with_setup(my_setup, my_teardown) @with_setup(my_setup, my_teardown)
def test_hello(): def test_hello():
print (l) print (values)
assert l == [1] assert values == [1]
def test_world(): def test_world():
print (l) print (values)
assert l == [1,2] assert values == [1,2]
""") """)
result = testdir.runpytest(p, '-p', 'nose') result = testdir.runpytest(p, '-p', 'nose')
@ -101,13 +101,13 @@ def test_nose_setup_func_failure(testdir):
def test_nose_setup_func_failure_2(testdir): def test_nose_setup_func_failure_2(testdir):
testdir.makepyfile(""" testdir.makepyfile("""
l = [] values = []
my_setup = 1 my_setup = 1
my_teardown = 2 my_teardown = 2
def test_hello(): def test_hello():
assert l == [] assert values == []
test_hello.setup = my_setup test_hello.setup = my_setup
test_hello.teardown = my_teardown test_hello.teardown = my_teardown
@ -121,26 +121,26 @@ def test_nose_setup_partial(testdir):
p = testdir.makepyfile(""" p = testdir.makepyfile("""
from functools import partial from functools import partial
l = [] values = []
def my_setup(x): def my_setup(x):
a = x a = x
l.append(a) values.append(a)
def my_teardown(x): def my_teardown(x):
b = x b = x
l.append(b) values.append(b)
my_setup_partial = partial(my_setup, 1) my_setup_partial = partial(my_setup, 1)
my_teardown_partial = partial(my_teardown, 2) my_teardown_partial = partial(my_teardown, 2)
def test_hello(): def test_hello():
print (l) print (values)
assert l == [1] assert values == [1]
def test_world(): def test_world():
print (l) print (values)
assert l == [1,2] assert values == [1,2]
test_hello.setup = my_setup_partial test_hello.setup = my_setup_partial
test_hello.teardown = my_teardown_partial test_hello.teardown = my_teardown_partial
@ -251,19 +251,19 @@ def test_module_level_setup(testdir):
def test_nose_style_setup_teardown(testdir): def test_nose_style_setup_teardown(testdir):
testdir.makepyfile(""" testdir.makepyfile("""
l = [] values = []
def setup_module(): def setup_module():
l.append(1) values.append(1)
def teardown_module(): def teardown_module():
del l[0] del values[0]
def test_hello(): def test_hello():
assert l == [1] assert values == [1]
def test_world(): def test_world():
assert l == [1] assert values == [1]
""") """)
result = testdir.runpytest('-p', 'nose') result = testdir.runpytest('-p', 'nose')
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([

View File

@ -85,23 +85,23 @@ class TestPytestPluginInteractions(object):
def test_configure(self, testdir): def test_configure(self, testdir):
config = testdir.parseconfig() config = testdir.parseconfig()
l = [] values = []
class A(object): class A(object):
def pytest_configure(self, config): def pytest_configure(self, config):
l.append(self) values.append(self)
config.pluginmanager.register(A()) config.pluginmanager.register(A())
assert len(l) == 0 assert len(values) == 0
config._do_configure() config._do_configure()
assert len(l) == 1 assert len(values) == 1
config.pluginmanager.register(A()) # leads to a configured() plugin config.pluginmanager.register(A()) # leads to a configured() plugin
assert len(l) == 2 assert len(values) == 2
assert l[0] != l[1] assert values[0] != values[1]
config._ensure_unconfigure() config._ensure_unconfigure()
config.pluginmanager.register(A()) config.pluginmanager.register(A())
assert len(l) == 2 assert len(values) == 2
def test_hook_tracing(self): def test_hook_tracing(self):
pytestpm = get_config().pluginmanager # fully initialized with plugins pytestpm = get_config().pluginmanager # fully initialized with plugins
@ -116,19 +116,19 @@ class TestPytestPluginInteractions(object):
saveindent.append(pytestpm.trace.root.indent) saveindent.append(pytestpm.trace.root.indent)
raise ValueError() raise ValueError()
l = [] values = []
pytestpm.trace.root.setwriter(l.append) pytestpm.trace.root.setwriter(values.append)
undo = pytestpm.enable_tracing() undo = pytestpm.enable_tracing()
try: try:
indent = pytestpm.trace.root.indent indent = pytestpm.trace.root.indent
p = api1() p = api1()
pytestpm.register(p) pytestpm.register(p)
assert pytestpm.trace.root.indent == indent assert pytestpm.trace.root.indent == indent
assert len(l) >= 2 assert len(values) >= 2
assert 'pytest_plugin_registered' in l[0] assert 'pytest_plugin_registered' in values[0]
assert 'finish' in l[1] assert 'finish' in values[1]
l[:] = [] values[:] = []
with pytest.raises(ValueError): with pytest.raises(ValueError):
pytestpm.register(api2()) pytestpm.register(api2())
assert pytestpm.trace.root.indent == indent assert pytestpm.trace.root.indent == indent
@ -230,12 +230,12 @@ class TestPytestPluginManager(object):
mod = py.std.types.ModuleType("x.y.pytest_hello") mod = py.std.types.ModuleType("x.y.pytest_hello")
pm.register(mod) pm.register(mod)
assert pm.is_registered(mod) assert pm.is_registered(mod)
l = pm.get_plugins() values = pm.get_plugins()
assert mod in l assert mod in values
pytest.raises(ValueError, "pm.register(mod)") pytest.raises(ValueError, "pm.register(mod)")
pytest.raises(ValueError, lambda: pm.register(mod)) pytest.raises(ValueError, lambda: pm.register(mod))
# assert not pm.is_registered(mod2) # assert not pm.is_registered(mod2)
assert pm.get_plugins() == l assert pm.get_plugins() == values
def test_canonical_import(self, monkeypatch): def test_canonical_import(self, monkeypatch):
mod = py.std.types.ModuleType("pytest_xyz") mod = py.std.types.ModuleType("pytest_xyz")
@ -269,8 +269,8 @@ class TestPytestPluginManager(object):
# check that it is not registered twice # check that it is not registered twice
pytestpm.consider_module(mod) pytestpm.consider_module(mod)
l = reprec.getcalls("pytest_plugin_registered") values = reprec.getcalls("pytest_plugin_registered")
assert len(l) == 1 assert len(values) == 1
def test_consider_env_fails_to_import(self, monkeypatch, pytestpm): def test_consider_env_fails_to_import(self, monkeypatch, pytestpm):
monkeypatch.setenv('PYTEST_PLUGINS', 'nonexisting', prepend=",") monkeypatch.setenv('PYTEST_PLUGINS', 'nonexisting', prepend=",")

View File

@ -30,10 +30,10 @@ class TestWarningsRecorderChecker(object):
assert len(rec.list) == 2 assert len(rec.list) == 2
warn = rec.pop() warn = rec.pop()
assert str(warn.message) == "hello" assert str(warn.message) == "hello"
l = rec.list values = rec.list
rec.clear() rec.clear()
assert len(rec.list) == 0 assert len(rec.list) == 0
assert l is rec.list assert values is rec.list
pytest.raises(AssertionError, "rec.pop()") pytest.raises(AssertionError, "rec.pop()")
def test_typechecking(self): def test_typechecking(self):

View File

@ -13,12 +13,12 @@ class TestSetupState(object):
def test_setup(self, testdir): def test_setup(self, testdir):
ss = runner.SetupState() ss = runner.SetupState()
item = testdir.getitem("def test_func(): pass") item = testdir.getitem("def test_func(): pass")
l = [1] values = [1]
ss.prepare(item) ss.prepare(item)
ss.addfinalizer(l.pop, colitem=item) ss.addfinalizer(values.pop, colitem=item)
assert l assert values
ss._pop_and_teardown() ss._pop_and_teardown()
assert not l assert not values
def test_teardown_exact_stack_empty(self, testdir): def test_teardown_exact_stack_empty(self, testdir):
item = testdir.getitem("def test_func(): pass") item = testdir.getitem("def test_func(): pass")

View File

@ -39,20 +39,20 @@ def test_module_and_function_setup(testdir):
def test_module_setup_failure_no_teardown(testdir): def test_module_setup_failure_no_teardown(testdir):
reprec = testdir.inline_runsource(""" reprec = testdir.inline_runsource("""
l = [] values = []
def setup_module(module): def setup_module(module):
l.append(1) values.append(1)
0/0 0/0
def test_nothing(): def test_nothing():
pass pass
def teardown_module(module): def teardown_module(module):
l.append(2) values.append(2)
""") """)
reprec.assertoutcome(failed=1) reprec.assertoutcome(failed=1)
calls = reprec.getcalls("pytest_runtest_setup") calls = reprec.getcalls("pytest_runtest_setup")
assert calls[0].item.module.l == [1] assert calls[0].item.module.values == [1]
def test_setup_function_failure_no_teardown(testdir): def test_setup_function_failure_no_teardown(testdir):

View File

@ -45,9 +45,9 @@ class SessionTests(object):
a = 1 a = 1
""") """)
reprec = testdir.inline_run(tfile) reprec = testdir.inline_run(tfile)
l = reprec.getfailedcollections() values = reprec.getfailedcollections()
assert len(l) == 1 assert len(values) == 1
out = str(l[0].longrepr) out = str(values[0].longrepr)
assert out.find('does_not_work') != -1 assert out.find('does_not_work') != -1
def test_raises_output(self, testdir): def test_raises_output(self, testdir):
@ -75,9 +75,9 @@ class SessionTests(object):
def test_syntax_error_module(self, testdir): def test_syntax_error_module(self, testdir):
reprec = testdir.inline_runsource("this is really not python") reprec = testdir.inline_runsource("this is really not python")
l = reprec.getfailedcollections() values = reprec.getfailedcollections()
assert len(l) == 1 assert len(values) == 1
out = str(l[0].longrepr) out = str(values[0].longrepr)
assert out.find(str('not python')) != -1 assert out.find(str('not python')) != -1
def test_exit_first_problem(self, testdir): def test_exit_first_problem(self, testdir):
@ -144,15 +144,15 @@ class TestNewSession(SessionTests):
def test_order_of_execution(self, testdir): def test_order_of_execution(self, testdir):
reprec = testdir.inline_runsource(""" reprec = testdir.inline_runsource("""
l = [] values = []
def test_1(): def test_1():
l.append(1) values.append(1)
def test_2(): def test_2():
l.append(2) values.append(2)
def test_3(): def test_3():
assert l == [1,2] assert values == [1,2]
class Testmygroup(object): class Testmygroup(object):
reslist = l reslist = values
def test_1(self): def test_1(self):
self.reslist.append(1) self.reslist.append(1)
def test_2(self): def test_2(self):
@ -242,13 +242,13 @@ def test_exclude(testdir):
def test_sessionfinish_with_start(testdir): def test_sessionfinish_with_start(testdir):
testdir.makeconftest(""" testdir.makeconftest("""
import os import os
l = [] values = []
def pytest_sessionstart(): def pytest_sessionstart():
l.append(os.getcwd()) values.append(os.getcwd())
os.chdir("..") os.chdir("..")
def pytest_sessionfinish(): def pytest_sessionfinish():
assert l[0] == os.getcwd() assert values[0] == os.getcwd()
""") """)
res = testdir.runpytest("--collect-only") res = testdir.runpytest("--collect-only")

View File

@ -679,9 +679,9 @@ def test_skip_reasons_folding():
ev2.longrepr = longrepr ev2.longrepr = longrepr
ev2.skipped = True ev2.skipped = True
l = folded_skips([ev1, ev2]) values = folded_skips([ev1, ev2])
assert len(l) == 1 assert len(values) == 1
num, fspath, lineno, reason = l[0] num, fspath, lineno, reason = values[0]
assert num == 2 assert num == 2
assert fspath == path assert fspath == path
assert lineno == lineno assert lineno == lineno

View File

@ -24,12 +24,12 @@ class Option(object):
@property @property
def args(self): def args(self):
l = [] values = []
if self.verbose: if self.verbose:
l.append('-v') values.append('-v')
if self.fulltrace: if self.fulltrace:
l.append('--fulltrace') values.append('--fulltrace')
return l return values
def pytest_generate_tests(metafunc): def pytest_generate_tests(metafunc):

View File

@ -73,19 +73,19 @@ def test_setup(testdir):
def test_setUpModule(testdir): def test_setUpModule(testdir):
testpath = testdir.makepyfile(""" testpath = testdir.makepyfile("""
l = [] values = []
def setUpModule(): def setUpModule():
l.append(1) values.append(1)
def tearDownModule(): def tearDownModule():
del l[0] del values[0]
def test_hello(): def test_hello():
assert l == [1] assert values == [1]
def test_world(): def test_world():
assert l == [1] assert values == [1]
""") """)
result = testdir.runpytest(testpath) result = testdir.runpytest(testpath)
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([
@ -95,13 +95,13 @@ def test_setUpModule(testdir):
def test_setUpModule_failing_no_teardown(testdir): def test_setUpModule_failing_no_teardown(testdir):
testpath = testdir.makepyfile(""" testpath = testdir.makepyfile("""
l = [] values = []
def setUpModule(): def setUpModule():
0/0 0/0
def tearDownModule(): def tearDownModule():
l.append(1) values.append(1)
def test_hello(): def test_hello():
pass pass
@ -109,7 +109,7 @@ def test_setUpModule_failing_no_teardown(testdir):
reprec = testdir.inline_run(testpath) reprec = testdir.inline_run(testpath)
reprec.assertoutcome(passed=0, failed=1) reprec.assertoutcome(passed=0, failed=1)
call = reprec.getcalls("pytest_runtest_setup")[0] call = reprec.getcalls("pytest_runtest_setup")[0]
assert not call.item.module.l assert not call.item.module.values
def test_new_instances(testdir): def test_new_instances(testdir):
@ -129,14 +129,14 @@ def test_teardown(testdir):
testpath = testdir.makepyfile(""" testpath = testdir.makepyfile("""
import unittest import unittest
class MyTestCase(unittest.TestCase): class MyTestCase(unittest.TestCase):
l = [] values = []
def test_one(self): def test_one(self):
pass pass
def tearDown(self): def tearDown(self):
self.l.append(None) self.values.append(None)
class Second(unittest.TestCase): class Second(unittest.TestCase):
def test_check(self): def test_check(self):
self.assertEqual(MyTestCase.l, [None]) self.assertEqual(MyTestCase.values, [None])
""") """)
reprec = testdir.inline_run(testpath) reprec = testdir.inline_run(testpath)
passed, skipped, failed = reprec.countoutcomes() passed, skipped, failed = reprec.countoutcomes()

View File

@ -144,6 +144,8 @@ def test_unicode(testdir, pyfile_with_warnings):
@pytest.mark.skipif(sys.version_info >= (3, 0), @pytest.mark.skipif(sys.version_info >= (3, 0),
reason='warnings message is broken as it is not str instance') reason='warnings message is broken as it is not str instance')
def test_py2_unicode(testdir, pyfile_with_warnings): def test_py2_unicode(testdir, pyfile_with_warnings):
if getattr(sys, "pypy_version_info", ())[:2] == (5, 9) and sys.platform.startswith('win'):
pytest.xfail("fails with unicode error on PyPy2 5.9 and Windows (#2905)")
testdir.makepyfile(''' testdir.makepyfile('''
# -*- coding: utf8 -*- # -*- coding: utf8 -*-
import warnings import warnings

18
tox.ini
View File

@ -12,7 +12,7 @@ envlist =
py36 py36
py37 py37
pypy pypy
{py27,py35}-{pexpect,xdist,trial,numpy} {py27,py36}-{pexpect,xdist,trial,numpy}
py27-nobyte py27-nobyte
doctesting doctesting
py35-freeze py35-freeze
@ -37,7 +37,6 @@ deps =
[testenv:py27-subprocess] [testenv:py27-subprocess]
changedir = . changedir = .
basepython = python2.7
deps = deps =
pytest-xdist>=1.13 pytest-xdist>=1.13
mock mock
@ -65,10 +64,11 @@ deps =
mock mock
nose nose
hypothesis>=3.5.2 hypothesis>=3.5.2
changedir=testing
commands = commands =
pytest -n1 -rfsxX {posargs:testing} pytest -n1 -rfsxX {posargs:.}
[testenv:py35-xdist] [testenv:py36-xdist]
deps = {[testenv:py27-xdist]deps} deps = {[testenv:py27-xdist]deps}
commands = commands =
pytest -n3 -rfsxX {posargs:testing} pytest -n3 -rfsxX {posargs:testing}
@ -80,7 +80,7 @@ deps = pexpect
commands = commands =
pytest -rfsxX test_pdb.py test_terminal.py test_unittest.py pytest -rfsxX test_pdb.py test_terminal.py test_unittest.py
[testenv:py35-pexpect] [testenv:py36-pexpect]
changedir = testing changedir = testing
platform = linux|darwin platform = linux|darwin
deps = {[testenv:py27-pexpect]deps} deps = {[testenv:py27-pexpect]deps}
@ -92,17 +92,18 @@ deps =
pytest-xdist>=1.13 pytest-xdist>=1.13
hypothesis>=3.5.2 hypothesis>=3.5.2
distribute = true distribute = true
changedir=testing
setenv = setenv =
PYTHONDONTWRITEBYTECODE=1 PYTHONDONTWRITEBYTECODE=1
commands = commands =
pytest -n3 -rfsxX {posargs:testing} pytest -n3 -rfsxX {posargs:.}
[testenv:py27-trial] [testenv:py27-trial]
deps = twisted deps = twisted
commands = commands =
pytest -ra {posargs:testing/test_unittest.py} pytest -ra {posargs:testing/test_unittest.py}
[testenv:py35-trial] [testenv:py36-trial]
deps = {[testenv:py27-trial]deps} deps = {[testenv:py27-trial]deps}
commands = commands =
pytest -ra {posargs:testing/test_unittest.py} pytest -ra {posargs:testing/test_unittest.py}
@ -112,7 +113,7 @@ deps=numpy
commands= commands=
pytest -rfsxX {posargs:testing/python/approx.py} pytest -rfsxX {posargs:testing/python/approx.py}
[testenv:py35-numpy] [testenv:py36-numpy]
deps=numpy deps=numpy
commands= commands=
pytest -rfsxX {posargs:testing/python/approx.py} pytest -rfsxX {posargs:testing/python/approx.py}
@ -180,7 +181,6 @@ commands =
[testenv:coveralls] [testenv:coveralls]
passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH COVERALLS_REPO_TOKEN passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH COVERALLS_REPO_TOKEN
usedevelop = True usedevelop = True
basepython = python3.5
changedir = . changedir = .
deps = deps =
{[testenv]deps} {[testenv]deps}