Merge remote-tracking branch 'upstream/master' into merge-master-into-features
# Conflicts: # _pytest/capture.py # _pytest/compat.py # _pytest/python.py # testing/python/collect.py # testing/test_mark.py
This commit is contained in:
commit
f3b359f5b8
|
@ -31,7 +31,7 @@ env:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- env: TOXENV=py36
|
- env: TOXENV=py36
|
||||||
python: '3.6-dev'
|
python: '3.6'
|
||||||
- env: TOXENV=py37
|
- env: TOXENV=py37
|
||||||
python: 'nightly'
|
python: 'nightly'
|
||||||
allow_failures:
|
allow_failures:
|
||||||
|
|
5
AUTHORS
5
AUTHORS
|
@ -6,6 +6,7 @@ Contributors include::
|
||||||
Abdeali JK
|
Abdeali JK
|
||||||
Abhijeet Kasurde
|
Abhijeet Kasurde
|
||||||
Ahn Ki-Wook
|
Ahn Ki-Wook
|
||||||
|
Alexander Johnson
|
||||||
Alexei Kozlenok
|
Alexei Kozlenok
|
||||||
Anatoly Bubenkoff
|
Anatoly Bubenkoff
|
||||||
Andreas Zeidler
|
Andreas Zeidler
|
||||||
|
@ -86,6 +87,7 @@ Justyna Janczyszyn
|
||||||
Kale Kundert
|
Kale Kundert
|
||||||
Katarzyna Jachim
|
Katarzyna Jachim
|
||||||
Kevin Cox
|
Kevin Cox
|
||||||
|
Kodi B. Arfer
|
||||||
Lee Kamentsky
|
Lee Kamentsky
|
||||||
Lev Maximov
|
Lev Maximov
|
||||||
Loic Esteve
|
Loic Esteve
|
||||||
|
@ -122,6 +124,7 @@ Oliver Bestwalter
|
||||||
Omar Kohl
|
Omar Kohl
|
||||||
Omer Hadari
|
Omer Hadari
|
||||||
Patrick Hayes
|
Patrick Hayes
|
||||||
|
Paweł Adamczak
|
||||||
Pieter Mulder
|
Pieter Mulder
|
||||||
Piotr Banaszkiewicz
|
Piotr Banaszkiewicz
|
||||||
Punyashloka Biswal
|
Punyashloka Biswal
|
||||||
|
@ -140,6 +143,7 @@ Russel Winder
|
||||||
Ryan Wooden
|
Ryan Wooden
|
||||||
Samuele Pedroni
|
Samuele Pedroni
|
||||||
Simon Gomizelj
|
Simon Gomizelj
|
||||||
|
Skylar Downes
|
||||||
Stefan Farmbauer
|
Stefan Farmbauer
|
||||||
Stefan Zimmermann
|
Stefan Zimmermann
|
||||||
Stefano Taschini
|
Stefano Taschini
|
||||||
|
@ -155,5 +159,6 @@ Vasily Kuznetsov
|
||||||
Victor Uriarte
|
Victor Uriarte
|
||||||
Vlad Dragos
|
Vlad Dragos
|
||||||
Vidar T. Fauske
|
Vidar T. Fauske
|
||||||
|
Vitaly Lashmanov
|
||||||
Wouter van Ackooy
|
Wouter van Ackooy
|
||||||
Xuecong Liao
|
Xuecong Liao
|
||||||
|
|
|
@ -121,7 +121,22 @@ Bug Fixes
|
||||||
3.0.8 (unreleased)
|
3.0.8 (unreleased)
|
||||||
==================
|
==================
|
||||||
|
|
||||||
*
|
* Change capture.py's ``DontReadFromInput`` class to throw ``io.UnsupportedOperation`` errors rather
|
||||||
|
than ValueErrors in the ``fileno`` method (`#2276`_).
|
||||||
|
Thanks `@metasyn`_ for the PR.
|
||||||
|
|
||||||
|
* Fix exception formatting while importing modules when the exception message
|
||||||
|
contains non-ascii characters (`#2336`_).
|
||||||
|
Thanks `@fabioz`_ for the report and `@nicoddemus`_ for the PR.
|
||||||
|
|
||||||
|
* Added documentation related to issue (`#1937`_)
|
||||||
|
Thanks `@skylarjhdownes`_ for the PR.
|
||||||
|
|
||||||
|
* Allow collecting files with any file extension as Python modules (`#2369`_).
|
||||||
|
Thanks `@Kodiologist`_ for the PR.
|
||||||
|
|
||||||
|
* Show the correct error message when collect "parametrize" func with wrong args (`#2383`_).
|
||||||
|
Thanks `@The-Compiler`_ for the report and `@robin0371`_ for the PR.
|
||||||
|
|
||||||
*
|
*
|
||||||
|
|
||||||
|
@ -129,7 +144,20 @@ Bug Fixes
|
||||||
|
|
||||||
*
|
*
|
||||||
|
|
||||||
*
|
|
||||||
|
|
||||||
|
.. _@skylarjhdownes: https://github.com/skylarjhdownes
|
||||||
|
.. _@fabioz: https://github.com/fabioz
|
||||||
|
.. _@metasyn: https://github.com/metasyn
|
||||||
|
.. _@Kodiologist: https://github.com/Kodiologist
|
||||||
|
.. _@robin0371: https://github.com/robin0371
|
||||||
|
|
||||||
|
|
||||||
|
.. _#1937: https://github.com/pytest-dev/pytest/issues/1937
|
||||||
|
.. _#2276: https://github.com/pytest-dev/pytest/issues/2276
|
||||||
|
.. _#2336: https://github.com/pytest-dev/pytest/issues/2336
|
||||||
|
.. _#2369: https://github.com/pytest-dev/pytest/issues/2369
|
||||||
|
.. _#2383: https://github.com/pytest-dev/pytest/issues/2383
|
||||||
|
|
||||||
|
|
||||||
3.0.7 (2017-03-14)
|
3.0.7 (2017-03-14)
|
||||||
|
@ -138,7 +166,7 @@ Bug Fixes
|
||||||
|
|
||||||
* Fix issue in assertion rewriting breaking due to modules silently discarding
|
* Fix issue in assertion rewriting breaking due to modules silently discarding
|
||||||
other modules when importing fails
|
other modules when importing fails
|
||||||
Notably, importing the `anydbm` module is fixed. (`#2248`_).
|
Notably, importing the ``anydbm`` module is fixed. (`#2248`_).
|
||||||
Thanks `@pfhayes`_ for the PR.
|
Thanks `@pfhayes`_ for the PR.
|
||||||
|
|
||||||
* junitxml: Fix problematic case where system-out tag occured twice per testcase
|
* junitxml: Fix problematic case where system-out tag occured twice per testcase
|
||||||
|
@ -401,6 +429,7 @@ Bug Fixes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
3.0.2 (2016-09-01)
|
3.0.2 (2016-09-01)
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
|
15
README.rst
15
README.rst
|
@ -6,13 +6,20 @@
|
||||||
------
|
------
|
||||||
|
|
||||||
.. image:: https://img.shields.io/pypi/v/pytest.svg
|
.. image:: https://img.shields.io/pypi/v/pytest.svg
|
||||||
:target: https://pypi.python.org/pypi/pytest
|
:target: https://pypi.python.org/pypi/pytest
|
||||||
|
|
||||||
|
.. image:: https://anaconda.org/conda-forge/pytest/badges/version.svg
|
||||||
|
:target: https://anaconda.org/conda-forge/pytest
|
||||||
|
|
||||||
.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
|
.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
|
||||||
:target: https://pypi.python.org/pypi/pytest
|
:target: https://pypi.python.org/pypi/pytest
|
||||||
|
|
||||||
.. image:: https://img.shields.io/coveralls/pytest-dev/pytest/master.svg
|
.. image:: https://img.shields.io/coveralls/pytest-dev/pytest/master.svg
|
||||||
:target: https://coveralls.io/r/pytest-dev/pytest
|
:target: https://coveralls.io/r/pytest-dev/pytest
|
||||||
|
|
||||||
.. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master
|
.. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master
|
||||||
:target: https://travis-ci.org/pytest-dev/pytest
|
:target: https://travis-ci.org/pytest-dev/pytest
|
||||||
|
|
||||||
.. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true
|
.. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true
|
||||||
:target: https://ci.appveyor.com/project/pytestbot/pytest
|
:target: https://ci.appveyor.com/project/pytestbot/pytest
|
||||||
|
|
||||||
|
@ -34,7 +41,7 @@ An example of a simple test:
|
||||||
To execute it::
|
To execute it::
|
||||||
|
|
||||||
$ pytest
|
$ pytest
|
||||||
============================= test session starts =============================
|
============================= test session starts =============================
|
||||||
collected 1 items
|
collected 1 items
|
||||||
|
|
||||||
test_sample.py F
|
test_sample.py F
|
||||||
|
|
|
@ -3,13 +3,14 @@ import sys
|
||||||
from inspect import CO_VARARGS, CO_VARKEYWORDS
|
from inspect import CO_VARARGS, CO_VARKEYWORDS
|
||||||
import re
|
import re
|
||||||
from weakref import ref
|
from weakref import ref
|
||||||
|
from _pytest.compat import _PY2, _PY3, PY35
|
||||||
|
|
||||||
import py
|
import py
|
||||||
builtin_repr = repr
|
builtin_repr = repr
|
||||||
|
|
||||||
reprlib = py.builtin._tryimport('repr', 'reprlib')
|
reprlib = py.builtin._tryimport('repr', 'reprlib')
|
||||||
|
|
||||||
if sys.version_info[0] >= 3:
|
if _PY3:
|
||||||
from traceback import format_exception_only
|
from traceback import format_exception_only
|
||||||
else:
|
else:
|
||||||
from ._py2traceback import format_exception_only
|
from ._py2traceback import format_exception_only
|
||||||
|
@ -353,7 +354,7 @@ class ExceptionInfo(object):
|
||||||
help for navigating the traceback.
|
help for navigating the traceback.
|
||||||
"""
|
"""
|
||||||
_striptext = ''
|
_striptext = ''
|
||||||
_assert_start_repr = "AssertionError(u\'assert " if sys.version_info[0] < 3 else "AssertionError(\'assert "
|
_assert_start_repr = "AssertionError(u\'assert " if _PY2 else "AssertionError(\'assert "
|
||||||
|
|
||||||
def __init__(self, tup=None, exprinfo=None):
|
def __init__(self, tup=None, exprinfo=None):
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
|
@ -618,7 +619,7 @@ class FormattedExcinfo(object):
|
||||||
|
|
||||||
|
|
||||||
def repr_excinfo(self, excinfo):
|
def repr_excinfo(self, excinfo):
|
||||||
if sys.version_info[0] < 3:
|
if _PY2:
|
||||||
reprtraceback = self.repr_traceback(excinfo)
|
reprtraceback = self.repr_traceback(excinfo)
|
||||||
reprcrash = excinfo._getreprcrash()
|
reprcrash = excinfo._getreprcrash()
|
||||||
|
|
||||||
|
@ -655,7 +656,7 @@ class FormattedExcinfo(object):
|
||||||
class TerminalRepr(object):
|
class TerminalRepr(object):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
s = self.__unicode__()
|
s = self.__unicode__()
|
||||||
if sys.version_info[0] < 3:
|
if _PY2:
|
||||||
s = s.encode('utf-8')
|
s = s.encode('utf-8')
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
@ -851,7 +852,7 @@ def getrawcode(obj, trycall=True):
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
if sys.version_info[:2] >= (3, 5): # RecursionError introduced in 3.5
|
if PY35: # RecursionError introduced in 3.5
|
||||||
def is_recursion_error(excinfo):
|
def is_recursion_error(excinfo):
|
||||||
return excinfo.errisinstance(RecursionError) # noqa
|
return excinfo.errisinstance(RecursionError) # noqa
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -28,6 +28,7 @@ _PY2 = not _PY3
|
||||||
NoneType = type(None)
|
NoneType = type(None)
|
||||||
NOTSET = object()
|
NOTSET = object()
|
||||||
|
|
||||||
|
PY35 = sys.version_info[:2] >= (3, 5)
|
||||||
PY36 = sys.version_info[:2] >= (3, 6)
|
PY36 = sys.version_info[:2] >= (3, 6)
|
||||||
MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError'
|
MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError'
|
||||||
|
|
||||||
|
@ -250,8 +251,10 @@ else:
|
||||||
try:
|
try:
|
||||||
return str(v)
|
return str(v)
|
||||||
except UnicodeError:
|
except UnicodeError:
|
||||||
|
if not isinstance(v, unicode):
|
||||||
|
v = unicode(v)
|
||||||
errors = 'replace'
|
errors = 'replace'
|
||||||
return v.encode('ascii', errors)
|
return v.encode('utf-8', errors)
|
||||||
|
|
||||||
|
|
||||||
COLLECT_FAKEMODULE_ATTRIBUTES = (
|
COLLECT_FAKEMODULE_ATTRIBUTES = (
|
||||||
|
|
|
@ -826,8 +826,8 @@ class FixtureFunctionMarker(object):
|
||||||
def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
|
def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
|
||||||
""" (return a) decorator to mark a fixture factory function.
|
""" (return a) decorator to mark a fixture factory function.
|
||||||
|
|
||||||
This decorator can be used (with or or without parameters) to define
|
This decorator can be used (with or without parameters) to define a
|
||||||
a fixture function. The name of the fixture function can later be
|
fixture function. The name of the fixture function can later be
|
||||||
referenced to cause its invocation ahead of running tests: test
|
referenced to cause its invocation ahead of running tests: test
|
||||||
modules or classes can use the pytest.mark.usefixtures(fixturename)
|
modules or classes can use the pytest.mark.usefixtures(fixturename)
|
||||||
marker. Test functions can directly use fixture names as input
|
marker. Test functions can directly use fixture names as input
|
||||||
|
@ -846,16 +846,16 @@ def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
|
||||||
reference is needed to activate the fixture.
|
reference is needed to activate the fixture.
|
||||||
|
|
||||||
:arg ids: list of string ids each corresponding to the params
|
:arg ids: list of string ids each corresponding to the params
|
||||||
so that they are part of the test id. If no ids are provided
|
so that they are part of the test id. If no ids are provided
|
||||||
they will be generated automatically from the params.
|
they will be generated automatically from the params.
|
||||||
|
|
||||||
:arg name: the name of the fixture. This defaults to the name of the
|
:arg name: the name of the fixture. This defaults to the name of the
|
||||||
decorated function. If a fixture is used in the same module in
|
decorated function. If a fixture is used in the same module in
|
||||||
which it is defined, the function name of the fixture will be
|
which it is defined, the function name of the fixture will be
|
||||||
shadowed by the function arg that requests the fixture; one way
|
shadowed by the function arg that requests the fixture; one way
|
||||||
to resolve this is to name the decorated function
|
to resolve this is to name the decorated function
|
||||||
``fixture_<fixturename>`` and then use
|
``fixture_<fixturename>`` and then use
|
||||||
``@pytest.fixture(name='<fixturename>')``.
|
``@pytest.fixture(name='<fixturename>')``.
|
||||||
|
|
||||||
Fixtures can optionally provide their values to test functions using a ``yield`` statement,
|
Fixtures can optionally provide their values to test functions using a ``yield`` statement,
|
||||||
instead of ``return``. In this case, the code block after the ``yield`` statement is executed
|
instead of ``return``. In this case, the code block after the ``yield`` statement is executed
|
||||||
|
|
|
@ -452,9 +452,10 @@ class Testdir(object):
|
||||||
the module is re-imported.
|
the module is re-imported.
|
||||||
"""
|
"""
|
||||||
for name in set(sys.modules).difference(self._savemodulekeys):
|
for name in set(sys.modules).difference(self._savemodulekeys):
|
||||||
# zope.interface (used by twisted-related tests) keeps internal
|
# some zope modules used by twisted-related tests keeps internal
|
||||||
# state and can't be deleted
|
# state and can't be deleted; we had some trouble in the past
|
||||||
if not name.startswith("zope.interface"):
|
# with zope.interface for example
|
||||||
|
if not name.startswith("zope"):
|
||||||
del sys.modules[name]
|
del sys.modules[name]
|
||||||
|
|
||||||
def make_hook_recorder(self, pluginmanager):
|
def make_hook_recorder(self, pluginmanager):
|
||||||
|
|
|
@ -4,6 +4,7 @@ from __future__ import absolute_import, division, print_function
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import inspect
|
import inspect
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
import collections
|
import collections
|
||||||
import math
|
import math
|
||||||
from itertools import count
|
from itertools import count
|
||||||
|
@ -20,7 +21,7 @@ from _pytest.compat import (
|
||||||
isclass, isfunction, is_generator, _escape_strings,
|
isclass, isfunction, is_generator, _escape_strings,
|
||||||
REGEX_TYPE, STRING_TYPES, NoneType, NOTSET,
|
REGEX_TYPE, STRING_TYPES, NoneType, NOTSET,
|
||||||
get_real_func, getfslineno, safe_getattr,
|
get_real_func, getfslineno, safe_getattr,
|
||||||
getlocation, enum,
|
safe_str, getlocation, enum,
|
||||||
)
|
)
|
||||||
from _pytest.runner import fail
|
from _pytest.runner import fail
|
||||||
|
|
||||||
|
@ -223,8 +224,7 @@ class PyobjMixin(PyobjContext):
|
||||||
continue
|
continue
|
||||||
name = node.name
|
name = node.name
|
||||||
if isinstance(node, Module):
|
if isinstance(node, Module):
|
||||||
assert name.endswith(".py")
|
name = os.path.splitext(name)[0]
|
||||||
name = name[:-3]
|
|
||||||
if stopatmodule:
|
if stopatmodule:
|
||||||
if includemodule:
|
if includemodule:
|
||||||
parts.append(name)
|
parts.append(name)
|
||||||
|
@ -427,7 +427,7 @@ class Module(main.File, PyCollector):
|
||||||
if self.config.getoption('verbose') < 2:
|
if self.config.getoption('verbose') < 2:
|
||||||
exc_info.traceback = exc_info.traceback.filter(filter_traceback)
|
exc_info.traceback = exc_info.traceback.filter(filter_traceback)
|
||||||
exc_repr = exc_info.getrepr(style='short') if exc_info.traceback else exc_info.exconly()
|
exc_repr = exc_info.getrepr(style='short') if exc_info.traceback else exc_info.exconly()
|
||||||
formatted_tb = py._builtin._totext(exc_repr)
|
formatted_tb = safe_str(exc_repr)
|
||||||
raise self.CollectError(
|
raise self.CollectError(
|
||||||
"ImportError while importing test module '{fspath}'.\n"
|
"ImportError while importing test module '{fspath}'.\n"
|
||||||
"Hint: make sure your test modules/packages have valid Python names.\n"
|
"Hint: make sure your test modules/packages have valid Python names.\n"
|
||||||
|
@ -843,7 +843,11 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
|
||||||
for callspec in self._calls or [CallSpec2(self)]:
|
for callspec in self._calls or [CallSpec2(self)]:
|
||||||
elements = zip(ids, parameters, count())
|
elements = zip(ids, parameters, count())
|
||||||
for a_id, param, param_index in elements:
|
for a_id, param, param_index in elements:
|
||||||
assert len(param.values) == len(argnames)
|
if len(param.values) != len(argnames):
|
||||||
|
raise ValueError(
|
||||||
|
'In "parametrize" the number of values ({0}) must be '
|
||||||
|
'equal to the number of names ({1})'.format(
|
||||||
|
param.values, argnames))
|
||||||
newcallspec = callspec.copy(self)
|
newcallspec = callspec.copy(self)
|
||||||
newcallspec.setmulti(valtypes, argnames, param.values, a_id,
|
newcallspec.setmulti(valtypes, argnames, param.values, a_id,
|
||||||
param.deprecated_arg_dict, scopenum, param_index)
|
param.deprecated_arg_dict, scopenum, param_index)
|
||||||
|
|
|
@ -270,12 +270,21 @@ supporting modules which are not themselves test modules will not be rewritten.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
``pytest`` rewrites test modules on import. It does this by using an import
|
``pytest`` rewrites test modules on import by using an import
|
||||||
hook to write new pyc files. Most of the time this works transparently.
|
hook to write new ``pyc`` files. Most of the time this works transparently.
|
||||||
However, if you are messing with import yourself, the import hook may
|
However, if you are messing with import yourself, the import hook may
|
||||||
interfere. If this is the case, use ``--assert=plain``. Additionally,
|
interfere.
|
||||||
rewriting will fail silently if it cannot write new pycs, i.e. in a read-only
|
|
||||||
filesystem or a zipfile.
|
If this is the case you have two options:
|
||||||
|
|
||||||
|
* Disable rewriting for a specific module by adding the string
|
||||||
|
``PYTEST_DONT_REWRITE`` to its docstring.
|
||||||
|
|
||||||
|
* Disable rewriting for all modules by using ``--assert=plain``.
|
||||||
|
|
||||||
|
Additionally, rewriting will fail silently if it cannot write new ``.pyc`` files,
|
||||||
|
i.e. in a read-only filesystem or a zipfile.
|
||||||
|
|
||||||
|
|
||||||
For further information, Benjamin Peterson wrote up `Behind the scenes of pytest's new assertion rewriting <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_.
|
For further information, Benjamin Peterson wrote up `Behind the scenes of pytest's new assertion rewriting <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_.
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ Full pytest documentation
|
||||||
|
|
||||||
getting-started
|
getting-started
|
||||||
usage
|
usage
|
||||||
|
existingtestsuite
|
||||||
assert
|
assert
|
||||||
builtin
|
builtin
|
||||||
fixture
|
fixture
|
||||||
|
|
|
@ -45,11 +45,11 @@ Here is the algorithm which finds the rootdir from ``args``:
|
||||||
matched, it becomes the ini-file and its directory becomes the rootdir.
|
matched, it becomes the ini-file and its directory becomes the rootdir.
|
||||||
|
|
||||||
- if no ini-file was found, use the already determined common ancestor as root
|
- if no ini-file was found, use the already determined common ancestor as root
|
||||||
directory. This allows to work with pytest in structures that are not part of
|
directory. This allows the use of pytest in structures that are not part of
|
||||||
a package and don't have any particular ini-file configuration.
|
a package and don't have any particular ini-file configuration.
|
||||||
|
|
||||||
If no ``args`` are given, pytest collects test below the current working
|
If no ``args`` are given, pytest collects test below the current working
|
||||||
directory and also starts determining the rootdir from there.
|
directory and also starts determining the rootdir from there.
|
||||||
|
|
||||||
:warning: custom pytest plugin commandline arguments may include a path, as in
|
:warning: custom pytest plugin commandline arguments may include a path, as in
|
||||||
``pytest --log-output ../../test.log args``. Then ``args`` is mandatory,
|
``pytest --log-output ../../test.log args``. Then ``args`` is mandatory,
|
||||||
|
@ -97,6 +97,8 @@ check for ini-files as follows::
|
||||||
.. _`how to change command line options defaults`:
|
.. _`how to change command line options defaults`:
|
||||||
.. _`adding default options`:
|
.. _`adding default options`:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
How to change command line options defaults
|
How to change command line options defaults
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
.. _existingtestsuite:
|
||||||
|
|
||||||
|
Using pytest with an existing test suite
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
Pytest can be used with most existing test suites, but its
|
||||||
|
behavior differs from other test runners such as :ref:`nose <noseintegration>` or
|
||||||
|
Python's default unittest framework.
|
||||||
|
|
||||||
|
Before using this section you will want to :ref:`install pytest <getstarted>`.
|
||||||
|
|
||||||
|
Running an existing test suite with pytest
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
Say you want to contribute to an existing repository somewhere.
|
||||||
|
After pulling the code into your development space using some
|
||||||
|
flavor of version control and (optionally) setting up a virtualenv
|
||||||
|
you will want to run::
|
||||||
|
|
||||||
|
cd <repository>
|
||||||
|
pip install -e . # Environment dependent alternatives include
|
||||||
|
# 'python setup.py develop' and 'conda develop'
|
||||||
|
|
||||||
|
in your project root. This will set up a symlink to your code in
|
||||||
|
site-packages, allowing you to edit your code while your tests
|
||||||
|
run against it as if it were installed.
|
||||||
|
|
||||||
|
Setting up your project in development mode lets you avoid having to
|
||||||
|
reinstall every time you want to run your tests, and is less brittle than
|
||||||
|
mucking about with sys.path to point your tests at local code.
|
||||||
|
|
||||||
|
Also consider using :ref:`tox <use tox>`.
|
||||||
|
|
||||||
|
.. include:: links.inc
|
|
@ -253,7 +253,7 @@ the code after the *yield* statement serves as the teardown code:
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def smtp(request):
|
def smtp():
|
||||||
smtp = smtplib.SMTP("smtp.gmail.com")
|
smtp = smtplib.SMTP("smtp.gmail.com")
|
||||||
yield smtp # provide the fixture value
|
yield smtp # provide the fixture value
|
||||||
print("teardown smtp")
|
print("teardown smtp")
|
||||||
|
@ -287,7 +287,7 @@ Note that we can also seamlessly use the ``yield`` syntax with ``with`` statemen
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def smtp(request):
|
def smtp():
|
||||||
with smtplib.SMTP("smtp.gmail.com") as smtp:
|
with smtplib.SMTP("smtp.gmail.com") as smtp:
|
||||||
yield smtp # provide the fixture value
|
yield smtp # provide the fixture value
|
||||||
|
|
||||||
|
|
|
@ -49,17 +49,17 @@ That's it. You can execute the test function now::
|
||||||
platform linux -- Python 3.5.2, pytest-3.0.7, py-1.4.32, pluggy-0.4.0
|
platform linux -- Python 3.5.2, pytest-3.0.7, py-1.4.32, pluggy-0.4.0
|
||||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||||
collected 1 items
|
collected 1 items
|
||||||
|
|
||||||
test_sample.py F
|
test_sample.py F
|
||||||
|
|
||||||
======= FAILURES ========
|
======= FAILURES ========
|
||||||
_______ test_answer ________
|
_______ test_answer ________
|
||||||
|
|
||||||
def test_answer():
|
def test_answer():
|
||||||
> assert func(3) == 5
|
> assert func(3) == 5
|
||||||
E assert 4 == 5
|
E assert 4 == 5
|
||||||
E + where 4 = func(3)
|
E + where 4 = func(3)
|
||||||
|
|
||||||
test_sample.py:5: AssertionError
|
test_sample.py:5: AssertionError
|
||||||
======= 1 failed in 0.12 seconds ========
|
======= 1 failed in 0.12 seconds ========
|
||||||
|
|
||||||
|
@ -128,15 +128,15 @@ run the module by passing its filename::
|
||||||
.F
|
.F
|
||||||
======= FAILURES ========
|
======= FAILURES ========
|
||||||
_______ TestClass.test_two ________
|
_______ TestClass.test_two ________
|
||||||
|
|
||||||
self = <test_class.TestClass object at 0xdeadbeef>
|
self = <test_class.TestClass object at 0xdeadbeef>
|
||||||
|
|
||||||
def test_two(self):
|
def test_two(self):
|
||||||
x = "hello"
|
x = "hello"
|
||||||
> assert hasattr(x, 'check')
|
> assert hasattr(x, 'check')
|
||||||
E AssertionError: assert False
|
E AssertionError: assert False
|
||||||
E + where False = hasattr('hello', 'check')
|
E + where False = hasattr('hello', 'check')
|
||||||
|
|
||||||
test_class.py:8: AssertionError
|
test_class.py:8: AssertionError
|
||||||
1 failed, 1 passed in 0.12 seconds
|
1 failed, 1 passed in 0.12 seconds
|
||||||
|
|
||||||
|
@ -165,14 +165,14 @@ before performing the test function call. Let's just run it::
|
||||||
F
|
F
|
||||||
======= FAILURES ========
|
======= FAILURES ========
|
||||||
_______ test_needsfiles ________
|
_______ test_needsfiles ________
|
||||||
|
|
||||||
tmpdir = local('PYTEST_TMPDIR/test_needsfiles0')
|
tmpdir = local('PYTEST_TMPDIR/test_needsfiles0')
|
||||||
|
|
||||||
def test_needsfiles(tmpdir):
|
def test_needsfiles(tmpdir):
|
||||||
print (tmpdir)
|
print (tmpdir)
|
||||||
> assert 0
|
> assert 0
|
||||||
E assert 0
|
E assert 0
|
||||||
|
|
||||||
test_tmpdir.py:3: AssertionError
|
test_tmpdir.py:3: AssertionError
|
||||||
--------------------------- Captured stdout call ---------------------------
|
--------------------------- Captured stdout call ---------------------------
|
||||||
PYTEST_TMPDIR/test_needsfiles0
|
PYTEST_TMPDIR/test_needsfiles0
|
||||||
|
@ -192,6 +192,7 @@ Here are a few suggestions where to go next:
|
||||||
|
|
||||||
* :ref:`cmdline` for command line invocation examples
|
* :ref:`cmdline` for command line invocation examples
|
||||||
* :ref:`good practices <goodpractices>` for virtualenv, test layout
|
* :ref:`good practices <goodpractices>` for virtualenv, test layout
|
||||||
|
* :ref:`existingtestsuite` for working with pre-existing tests
|
||||||
* :ref:`fixtures` for providing a functional baseline to your tests
|
* :ref:`fixtures` for providing a functional baseline to your tests
|
||||||
* :ref:`plugins` managing and writing plugins
|
* :ref:`plugins` managing and writing plugins
|
||||||
|
|
||||||
|
|
|
@ -30,68 +30,106 @@ Within Python modules, ``pytest`` also discovers tests using the standard
|
||||||
|
|
||||||
|
|
||||||
Choosing a test layout / import rules
|
Choosing a test layout / import rules
|
||||||
------------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
``pytest`` supports two common test layouts:
|
``pytest`` supports two common test layouts:
|
||||||
|
|
||||||
* putting tests into an extra directory outside your actual application
|
Tests outside application code
|
||||||
code, useful if you have many functional tests or for other reasons
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
want to keep tests separate from actual application code (often a good
|
|
||||||
idea)::
|
|
||||||
|
|
||||||
setup.py # your setuptools Python package metadata
|
Putting tests into an extra directory outside your actual application code
|
||||||
|
might be useful if you have many functional tests or for other reasons want
|
||||||
|
to keep tests separate from actual application code (often a good idea)::
|
||||||
|
|
||||||
|
setup.py
|
||||||
mypkg/
|
mypkg/
|
||||||
__init__.py
|
__init__.py
|
||||||
appmodule.py
|
app.py
|
||||||
|
view.py
|
||||||
tests/
|
tests/
|
||||||
test_app.py
|
test_app.py
|
||||||
|
test_view.py
|
||||||
...
|
...
|
||||||
|
|
||||||
|
This way your tests can run easily against an installed version
|
||||||
|
of ``mypkg``.
|
||||||
|
|
||||||
* inlining test directories into your application package, useful if you
|
Note that using this scheme your test files must have **unique names**, because
|
||||||
have direct relation between (unit-)test and application modules and
|
``pytest`` will import them as *top-level* modules since there are no packages
|
||||||
want to distribute your tests along with your application::
|
to derive a full package name from. In other words, the test files in the example above will
|
||||||
|
be imported as ``test_app`` and ``test_view`` top-level modules by adding ``tests/`` to
|
||||||
|
``sys.path``.
|
||||||
|
|
||||||
setup.py # your setuptools Python package metadata
|
If you need to have test modules with the same name, you might add ``__init__.py`` files to your
|
||||||
|
``tests`` folder and subfolders, changing them to packages::
|
||||||
|
|
||||||
|
setup.py
|
||||||
|
mypkg/
|
||||||
|
...
|
||||||
|
tests/
|
||||||
|
__init__.py
|
||||||
|
foo/
|
||||||
|
__init__.py
|
||||||
|
test_view.py
|
||||||
|
bar/
|
||||||
|
__init__.py
|
||||||
|
test_view.py
|
||||||
|
|
||||||
|
Now pytest will load the modules as ``tests.foo.test_view`` and ``tests.bar.test_view``, allowing
|
||||||
|
you to have modules with the same name. But now this introduces a subtle problem: in order to load
|
||||||
|
the test modules from the ``tests`` directory, pytest prepends the root of the repository to
|
||||||
|
``sys.path``, which adds the side-effect that now ``mypkg`` is also importable.
|
||||||
|
This is problematic if you are using a tool like `tox`_ to test your package in a virtual environment,
|
||||||
|
because you want to test the *installed* version of your package, not the local code from the repository.
|
||||||
|
|
||||||
|
In this situation, it is **strongly** suggested to use a ``src`` layout where application root package resides in a
|
||||||
|
sub-directory of your root::
|
||||||
|
|
||||||
|
setup.py
|
||||||
|
src/
|
||||||
|
mypkg/
|
||||||
|
__init__.py
|
||||||
|
app.py
|
||||||
|
view.py
|
||||||
|
tests/
|
||||||
|
__init__.py
|
||||||
|
foo/
|
||||||
|
__init__.py
|
||||||
|
test_view.py
|
||||||
|
bar/
|
||||||
|
__init__.py
|
||||||
|
test_view.py
|
||||||
|
|
||||||
|
|
||||||
|
This layout prevents a lot of common pitfalls and has many benefits, which are better explained in this excellent
|
||||||
|
`blog post by Ionel Cristian Mărieș <https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure>`_.
|
||||||
|
|
||||||
|
Tests as part of application code
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Inlining test directories into your application package
|
||||||
|
is useful if you have direct relation between tests and application modules and
|
||||||
|
want to distribute them along with your application::
|
||||||
|
|
||||||
|
setup.py
|
||||||
mypkg/
|
mypkg/
|
||||||
__init__.py
|
__init__.py
|
||||||
appmodule.py
|
app.py
|
||||||
...
|
view.py
|
||||||
test/
|
test/
|
||||||
|
__init__.py
|
||||||
test_app.py
|
test_app.py
|
||||||
|
test_view.py
|
||||||
...
|
...
|
||||||
|
|
||||||
Important notes relating to both schemes:
|
In this scheme, it is easy to your run tests using the ``--pyargs`` option::
|
||||||
|
|
||||||
- **make sure that "mypkg" is importable**, for example by typing once::
|
pytest --pyargs mypkg
|
||||||
|
|
||||||
pip install -e . # install package using setup.py in editable mode
|
``pytest`` will discover where ``mypkg`` is installed and collect tests from there.
|
||||||
|
|
||||||
- **avoid "__init__.py" files in your test directories**.
|
Note that this layout also works in conjunction with the ``src`` layout mentioned in the previous section.
|
||||||
This way your tests can run easily against an installed version
|
|
||||||
of ``mypkg``, independently from the installed package if it contains
|
|
||||||
the tests or not.
|
|
||||||
|
|
||||||
- With inlined tests you might put ``__init__.py`` into test
|
|
||||||
directories and make them installable as part of your application.
|
|
||||||
Using the ``pytest --pyargs mypkg`` invocation pytest will
|
|
||||||
discover where mypkg is installed and collect tests from there.
|
|
||||||
With the "external" test you can still distribute tests but they
|
|
||||||
will not be installed or become importable.
|
|
||||||
|
|
||||||
Typically you can run tests by pointing to test directories or modules::
|
|
||||||
|
|
||||||
pytest tests/test_app.py # for external test dirs
|
|
||||||
pytest mypkg/test/test_app.py # for inlined test dirs
|
|
||||||
pytest mypkg # run tests in all below test directories
|
|
||||||
pytest # run all tests below current dir
|
|
||||||
...
|
|
||||||
|
|
||||||
Because of the above ``editable install`` mode you can change your
|
|
||||||
source code (both tests and the app) and rerun tests at will.
|
|
||||||
Once you are done with your work, you can `use tox`_ to make sure
|
|
||||||
that the package is really correct and tests pass in all
|
|
||||||
required configurations.
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
@ -144,7 +182,15 @@ for installing your application and any dependencies
|
||||||
as well as the ``pytest`` package itself. This ensures your code and
|
as well as the ``pytest`` package itself. This ensures your code and
|
||||||
dependencies are isolated from the system Python installation.
|
dependencies are isolated from the system Python installation.
|
||||||
|
|
||||||
If you frequently release code and want to make sure that your actual
|
You can then install your package in "editable" mode::
|
||||||
|
|
||||||
|
pip install -e .
|
||||||
|
|
||||||
|
which lets you change your source code (both tests and application) and rerun tests at will.
|
||||||
|
This is similar to running `python setup.py develop` or `conda develop` in that it installs
|
||||||
|
your package using a symlink to your development code.
|
||||||
|
|
||||||
|
Once you are done with your work and want to make sure that your actual
|
||||||
package passes all tests you may want to look into `tox`_, the
|
package passes all tests you may want to look into `tox`_, the
|
||||||
virtualenv test automation tool and its `pytest support
|
virtualenv test automation tool and its `pytest support
|
||||||
<https://tox.readthedocs.io/en/latest/example/pytest.html>`_.
|
<https://tox.readthedocs.io/en/latest/example/pytest.html>`_.
|
||||||
|
@ -154,11 +200,6 @@ options. It will run tests against the installed package and not
|
||||||
against your source code checkout, helping to detect packaging
|
against your source code checkout, helping to detect packaging
|
||||||
glitches.
|
glitches.
|
||||||
|
|
||||||
Continuous integration services such as Jenkins_ can make use of the
|
|
||||||
``--junitxml=PATH`` option to create a JUnitXML file and generate reports (e.g.
|
|
||||||
by publishing the results in a nice format with the `Jenkins xUnit Plugin
|
|
||||||
<https://wiki.jenkins-ci.org/display/JENKINS/xUnit+Plugin>`_).
|
|
||||||
|
|
||||||
|
|
||||||
Integrating with setuptools / ``python setup.py test`` / ``pytest-runner``
|
Integrating with setuptools / ``python setup.py test`` / ``pytest-runner``
|
||||||
--------------------------------------------------------------------------
|
--------------------------------------------------------------------------
|
||||||
|
|
|
@ -47,9 +47,19 @@ Unsupported idioms / known issues
|
||||||
``tests.test_mod``) but different file system paths
|
``tests.test_mod``) but different file system paths
|
||||||
(e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
|
(e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
|
||||||
by extending sys.path/import semantics. pytest does not do that
|
by extending sys.path/import semantics. pytest does not do that
|
||||||
but there is discussion in `issue268 <https://github.com/pytest-dev/pytest/issues/268>`_ for adding some support. Note that
|
but there is discussion in `#268 <https://github.com/pytest-dev/pytest/issues/268>`_ for adding some support. Note that
|
||||||
`nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.io/en/latest/differences.html#test-discovery-and-loading>`_.
|
`nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.io/en/latest/differences.html#test-discovery-and-loading>`_.
|
||||||
|
|
||||||
|
If you place a conftest.py file in the root directory of your project
|
||||||
|
(as determined by pytest) pytest will run tests "nose style" against
|
||||||
|
the code below that directory by adding it to your ``sys.path`` instead of
|
||||||
|
running against your installed code.
|
||||||
|
|
||||||
|
You may find yourself wanting to do this if you ran ``python setup.py install``
|
||||||
|
to set up your project, as opposed to ``python setup.py develop`` or any of
|
||||||
|
the package manager equivalents. Installing with develop in a
|
||||||
|
virtual environment like Tox is recommended over this pattern.
|
||||||
|
|
||||||
- nose-style doctests are not collected and executed correctly,
|
- nose-style doctests are not collected and executed correctly,
|
||||||
also doctest fixtures don't work.
|
also doctest fixtures don't work.
|
||||||
|
|
||||||
|
@ -62,3 +72,4 @@ Unsupported idioms / known issues
|
||||||
being the recommended alternative.
|
being the recommended alternative.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# pinning sphinx to 1.4.* due to search issues with rtd:
|
||||||
|
# https://github.com/rtfd/readthedocs-sphinx-ext/issues/25
|
||||||
|
sphinx ==1.4.*
|
|
@ -19,6 +19,18 @@ You can invoke testing through the Python interpreter from the command line::
|
||||||
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 python will also add the current directory to ``sys.path``.
|
||||||
|
|
||||||
|
Possible exit codes
|
||||||
|
--------------------------------------------------------------
|
||||||
|
|
||||||
|
Running ``pytest`` can result in six different exit codes:
|
||||||
|
|
||||||
|
:Exit code 0: All tests were collected and passed successfully
|
||||||
|
:Exit code 1: Tests were collected and run but some of the tests failed
|
||||||
|
:Exit code 2: Test execution was interrupted by the user
|
||||||
|
:Exit code 3: Internal error happened while executing tests
|
||||||
|
:Exit code 4: pytest command line usage error
|
||||||
|
:Exit code 5: No tests were collected
|
||||||
|
|
||||||
Getting help on version, option names, environment variables
|
Getting help on version, option names, environment variables
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,22 @@ class TestModule(object):
|
||||||
else:
|
else:
|
||||||
assert name not in stdout
|
assert name not in stdout
|
||||||
|
|
||||||
|
def test_show_traceback_import_error_unicode(self, testdir):
|
||||||
|
"""Check test modules collected which raise ImportError with unicode messages
|
||||||
|
are handled properly (#2336).
|
||||||
|
"""
|
||||||
|
testdir.makepyfile(u"""
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
raise ImportError(u'Something bad happened ☺')
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"ImportError while importing test module*",
|
||||||
|
"Traceback:",
|
||||||
|
"*raise ImportError*Something bad happened*",
|
||||||
|
])
|
||||||
|
assert result.ret == 2
|
||||||
|
|
||||||
|
|
||||||
class TestClass(object):
|
class TestClass(object):
|
||||||
def test_class_with_init_warning(self, testdir):
|
def test_class_with_init_warning(self, testdir):
|
||||||
|
@ -826,6 +842,34 @@ class TestConftestCustomization(object):
|
||||||
l = modcol.collect()
|
l = modcol.collect()
|
||||||
assert '_hello' not in l
|
assert '_hello' not in l
|
||||||
|
|
||||||
|
def test_issue2369_collect_module_fileext(self, testdir):
|
||||||
|
"""Ensure we can collect files with weird file extensions as Python
|
||||||
|
modules (#2369)"""
|
||||||
|
# We'll implement a little finder and loader to import files containing
|
||||||
|
# Python source code whose file extension is ".narf".
|
||||||
|
testdir.makeconftest("""
|
||||||
|
import sys, os, imp
|
||||||
|
from _pytest.python import Module
|
||||||
|
|
||||||
|
class Loader:
|
||||||
|
def load_module(self, name):
|
||||||
|
return imp.load_source(name, name + ".narf")
|
||||||
|
class Finder:
|
||||||
|
def find_module(self, name, path=None):
|
||||||
|
if os.path.exists(name + ".narf"):
|
||||||
|
return Loader()
|
||||||
|
sys.meta_path.append(Finder())
|
||||||
|
|
||||||
|
def pytest_collect_file(path, parent):
|
||||||
|
if path.ext == ".narf":
|
||||||
|
return Module(path, parent)""")
|
||||||
|
testdir.makefile(".narf", """
|
||||||
|
def test_something():
|
||||||
|
assert 1 + 1 == 2""")
|
||||||
|
# Use runpytest_subprocess, since we're futzing with sys.meta_path.
|
||||||
|
result = testdir.runpytest_subprocess()
|
||||||
|
result.stdout.fnmatch_lines('*1 passed*')
|
||||||
|
|
||||||
def test_setup_only_available_in_subdir(testdir):
|
def test_setup_only_available_in_subdir(testdir):
|
||||||
sub1 = testdir.mkpydir("sub1")
|
sub1 = testdir.mkpydir("sub1")
|
||||||
sub2 = testdir.mkpydir("sub2")
|
sub2 = testdir.mkpydir("sub2")
|
||||||
|
|
|
@ -5,6 +5,7 @@ from __future__ import with_statement
|
||||||
import pickle
|
import pickle
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from io import UnsupportedOperation
|
||||||
|
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
import py
|
import py
|
||||||
|
@ -671,7 +672,7 @@ def test_dontreadfrominput():
|
||||||
pytest.raises(IOError, f.read)
|
pytest.raises(IOError, f.read)
|
||||||
pytest.raises(IOError, f.readlines)
|
pytest.raises(IOError, f.readlines)
|
||||||
pytest.raises(IOError, iter, f)
|
pytest.raises(IOError, iter, f)
|
||||||
pytest.raises(ValueError, f.fileno)
|
pytest.raises(UnsupportedOperation, f.fileno)
|
||||||
f.close() # just for completeness
|
f.close() # just for completeness
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -305,6 +305,23 @@ def test_parametrized_collected_from_command_line(testdir):
|
||||||
rec.assertoutcome(passed=3)
|
rec.assertoutcome(passed=3)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parametrized_collect_with_wrong_args(testdir):
|
||||||
|
"""Test collect parametrized func with wrong number of args."""
|
||||||
|
py_file = testdir.makepyfile("""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('foo, bar', [(1, 2, 3)])
|
||||||
|
def test_func(foo, bar):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
|
||||||
|
result = testdir.runpytest(py_file)
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
'E ValueError: In "parametrize" the number of values ((1, 2, 3)) '
|
||||||
|
'must be equal to the number of names ([\'foo\', \'bar\'])'
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
class TestFunctional(object):
|
class TestFunctional(object):
|
||||||
|
|
||||||
def test_mark_per_function(self, testdir):
|
def test_mark_per_function(self, testdir):
|
||||||
|
|
Loading…
Reference in New Issue