Merged branch features into features
This commit is contained in:
commit
f7b5bb2f97
2
AUTHORS
2
AUTHORS
|
@ -100,6 +100,7 @@ Samuele Pedroni
|
||||||
Steffen Allner
|
Steffen Allner
|
||||||
Stephan Obermann
|
Stephan Obermann
|
||||||
Tareq Alayan
|
Tareq Alayan
|
||||||
|
Ted Xiao
|
||||||
Simon Gomizelj
|
Simon Gomizelj
|
||||||
Stefano Taschini
|
Stefano Taschini
|
||||||
Stefan Farmbauer
|
Stefan Farmbauer
|
||||||
|
@ -109,3 +110,4 @@ Trevor Bekolay
|
||||||
Vasily Kuznetsov
|
Vasily Kuznetsov
|
||||||
Wouter van Ackooy
|
Wouter van Ackooy
|
||||||
Bernard Pratz
|
Bernard Pratz
|
||||||
|
Stefan Zimmermann
|
||||||
|
|
|
@ -1,3 +1,40 @@
|
||||||
|
3.0.0.dev1
|
||||||
|
==========
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
* Fix (`#607`_): pytest.skip() is no longer allowed at module level to
|
||||||
|
prevent misleading use as test function decorator. When used at a module
|
||||||
|
level an error will be raised during collection.
|
||||||
|
Thanks `@omarkohl`_ for the complete PR (`#1519`_).
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
**Incompatible changes**
|
||||||
|
|
||||||
|
* Removing the following deprecated commandline options
|
||||||
|
|
||||||
|
* ``--genscript``
|
||||||
|
* ``--no-assert``
|
||||||
|
* ``--nomagic``
|
||||||
|
* ``--report``
|
||||||
|
|
||||||
|
Thanks to `@RedBeardCode`_ for the PR(`#1664`_)
|
||||||
|
|
||||||
|
* removed support code for python 3 < 3.3 addressing (`#1627`_)
|
||||||
|
|
||||||
|
.. _#607: https://github.com/pytest-dev/pytest/issues/607
|
||||||
|
.. _#1519: https://github.com/pytest-dev/pytest/pull/1519
|
||||||
|
.. _#1664: https://github.com/pytest-dev/pytest/pull/1664
|
||||||
|
.. _#1627: https://github.com/pytest-dev/pytest/pull/1627
|
||||||
|
|
||||||
|
|
||||||
2.10.0.dev1
|
2.10.0.dev1
|
||||||
===========
|
===========
|
||||||
|
|
||||||
|
@ -58,6 +95,19 @@
|
||||||
finalizer and has access to the fixture's result cache.
|
finalizer and has access to the fixture's result cache.
|
||||||
Thanks `@d6e`_, `@sallner`_
|
Thanks `@d6e`_, `@sallner`_
|
||||||
|
|
||||||
|
* Issue a warning for asserts whose test is a tuple literal. Such asserts will
|
||||||
|
never fail because tuples are always truthy and are usually a mistake
|
||||||
|
(see `#1562`_). Thanks `@kvas-it`_, for the PR.
|
||||||
|
|
||||||
|
* New cli flag ``--override-ini`` or ``-o`` that overrides values from the ini file.
|
||||||
|
Example '-o xfail_strict=True'. A complete ini-options can be viewed
|
||||||
|
by py.test --help. Thanks `@blueyed`_ and `@fengxx`_ for the PR
|
||||||
|
|
||||||
|
* Remove all py.test-X* entry points. The versioned, suffixed entry points
|
||||||
|
were never documented and a leftover from a pre-virtualenv era. These entry
|
||||||
|
points also created broken entry points in wheels, so removing them also
|
||||||
|
removes a source of confusion for users (`#1632`_).
|
||||||
|
Thanks `@obestwalter`_ for the PR.
|
||||||
|
|
||||||
**Changes**
|
**Changes**
|
||||||
|
|
||||||
|
@ -118,8 +168,11 @@
|
||||||
Thanks `@Vogtinator`_ for reporting. Thanks to `@RedBeardCode`_ and
|
Thanks `@Vogtinator`_ for reporting. Thanks to `@RedBeardCode`_ and
|
||||||
`@tomviner`_ for PR.
|
`@tomviner`_ for PR.
|
||||||
|
|
||||||
*
|
* Add proposal to docs for a new feature that enables users to combine multiple
|
||||||
|
fixtures into one. Thanks to `@hpk42`_ and `@hackebrot`_.
|
||||||
|
|
||||||
|
|
||||||
|
.. _#1632: https://github.com/pytest-dev/pytest/issues/1632
|
||||||
.. _#1580: https://github.com/pytest-dev/pytest/pull/1580
|
.. _#1580: https://github.com/pytest-dev/pytest/pull/1580
|
||||||
.. _#1605: https://github.com/pytest-dev/pytest/issues/1605
|
.. _#1605: https://github.com/pytest-dev/pytest/issues/1605
|
||||||
.. _#1597: https://github.com/pytest-dev/pytest/pull/1597
|
.. _#1597: https://github.com/pytest-dev/pytest/pull/1597
|
||||||
|
@ -131,6 +184,8 @@
|
||||||
.. _@nikratio: https://github.com/nikratio
|
.. _@nikratio: https://github.com/nikratio
|
||||||
.. _@RedBeardCode: https://github.com/RedBeardCode
|
.. _@RedBeardCode: https://github.com/RedBeardCode
|
||||||
.. _@Vogtinator: https://github.com/Vogtinator
|
.. _@Vogtinator: https://github.com/Vogtinator
|
||||||
|
.. _@blueyed: https://github.com/blueyed
|
||||||
|
.. _@fengxx: https://github.com/fengxx
|
||||||
|
|
||||||
* Fix `#1421`_: Exit tests if a collection error occurs and add
|
* Fix `#1421`_: Exit tests if a collection error occurs and add
|
||||||
``--continue-on-collection-errors`` option to restore previous behaviour.
|
``--continue-on-collection-errors`` option to restore previous behaviour.
|
||||||
|
@ -139,6 +194,11 @@
|
||||||
|
|
||||||
*
|
*
|
||||||
|
|
||||||
|
* ``OptionGroup.addoption()`` now checks if option names were already
|
||||||
|
added before, to make it easier to track down issues like `#1618`_.
|
||||||
|
Before, you only got exceptions later from ``argparse`` library,
|
||||||
|
giving no clue about the actual reason for double-added options.
|
||||||
|
|
||||||
.. _@milliams: https://github.com/milliams
|
.. _@milliams: https://github.com/milliams
|
||||||
.. _@csaftoiu: https://github.com/csaftoiu
|
.. _@csaftoiu: https://github.com/csaftoiu
|
||||||
.. _@flub: https://github.com/flub
|
.. _@flub: https://github.com/flub
|
||||||
|
@ -172,10 +232,12 @@
|
||||||
.. _#1619: https://github.com/pytest-dev/pytest/issues/1619
|
.. _#1619: https://github.com/pytest-dev/pytest/issues/1619
|
||||||
.. _#372: https://github.com/pytest-dev/pytest/issues/372
|
.. _#372: https://github.com/pytest-dev/pytest/issues/372
|
||||||
.. _#1544: https://github.com/pytest-dev/pytest/issues/1544
|
.. _#1544: https://github.com/pytest-dev/pytest/issues/1544
|
||||||
|
.. _#1562: https://github.com/pytest-dev/pytest/issues/1562
|
||||||
.. _#1616: https://github.com/pytest-dev/pytest/pull/1616
|
.. _#1616: https://github.com/pytest-dev/pytest/pull/1616
|
||||||
.. _#1628: https://github.com/pytest-dev/pytest/pull/1628
|
.. _#1628: https://github.com/pytest-dev/pytest/pull/1628
|
||||||
.. _#1629: https://github.com/pytest-dev/pytest/issues/1629
|
.. _#1629: https://github.com/pytest-dev/pytest/issues/1629
|
||||||
.. _#1633: https://github.com/pytest-dev/pytest/pull/1633
|
.. _#1633: https://github.com/pytest-dev/pytest/pull/1633
|
||||||
|
.. _#1618: https://github.com/pytest-dev/pytest/issues/1618
|
||||||
|
|
||||||
|
|
||||||
**Bug Fixes**
|
**Bug Fixes**
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
:target: https://ci.appveyor.com/project/pytestbot/pytest
|
:target: https://ci.appveyor.com/project/pytestbot/pytest
|
||||||
|
|
||||||
The ``pytest`` framework makes it easy to write small tests, yet
|
The ``pytest`` framework makes it easy to write small tests, yet
|
||||||
scales to support complex functional testing for applications and libraries.
|
scales to support complex functional testing for applications and libraries.
|
||||||
|
|
||||||
An example of a simple test:
|
An example of a simple test:
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ To execute it::
|
||||||
|
|
||||||
$ pytest
|
$ pytest
|
||||||
======= test session starts ========
|
======= test session starts ========
|
||||||
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
|
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
|
||||||
collected 1 items
|
collected 1 items
|
||||||
|
|
||||||
test_sample.py F
|
test_sample.py F
|
||||||
|
@ -52,7 +52,7 @@ To execute it::
|
||||||
======= 1 failed in 0.12 seconds ========
|
======= 1 failed in 0.12 seconds ========
|
||||||
|
|
||||||
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://pytest.org/latest/getting-started.html#our-first-test-run>`_ for more examples.
|
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://pytest.org/latest/getting-started.html#our-first-test-run>`_ for more examples.
|
||||||
|
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
@ -69,7 +69,7 @@ Features
|
||||||
- Can run `unittest <http://pytest.org/latest/unittest.html>`_ (or trial),
|
- Can run `unittest <http://pytest.org/latest/unittest.html>`_ (or trial),
|
||||||
`nose <http://pytest.org/latest/nose.html>`_ test suites out of the box;
|
`nose <http://pytest.org/latest/nose.html>`_ test suites out of the box;
|
||||||
|
|
||||||
- Python2.6+, Python3.2+, PyPy-2.3, Jython-2.5 (untested);
|
- Python2.6+, Python3.3+, PyPy-2.3, Jython-2.5 (untested);
|
||||||
|
|
||||||
- Rich plugin architecture, with over 150+ `external plugins <http://pytest.org/latest/plugins.html#installing-external-plugins-searching>`_ and thriving community;
|
- Rich plugin architecture, with over 150+ `external plugins <http://pytest.org/latest/plugins.html#installing-external-plugins-searching>`_ and thriving community;
|
||||||
|
|
||||||
|
|
|
@ -25,15 +25,6 @@ def pytest_addoption(parser):
|
||||||
'rewrite' (the default) rewrites assert
|
'rewrite' (the default) rewrites assert
|
||||||
statements in test modules on import to
|
statements in test modules on import to
|
||||||
provide assert expression information. """)
|
provide assert expression information. """)
|
||||||
group.addoption('--no-assert',
|
|
||||||
action="store_true",
|
|
||||||
default=False,
|
|
||||||
dest="noassert",
|
|
||||||
help="DEPRECATED equivalent to --assert=plain")
|
|
||||||
group.addoption('--nomagic', '--no-magic',
|
|
||||||
action="store_true",
|
|
||||||
default=False,
|
|
||||||
help="DEPRECATED equivalent to --assert=plain")
|
|
||||||
|
|
||||||
|
|
||||||
class AssertionState:
|
class AssertionState:
|
||||||
|
@ -48,10 +39,6 @@ class AssertionState:
|
||||||
def pytest_load_initial_conftests(early_config, parser, args):
|
def pytest_load_initial_conftests(early_config, parser, args):
|
||||||
ns, ns_unknown_args = parser.parse_known_and_unknown_args(args)
|
ns, ns_unknown_args = parser.parse_known_and_unknown_args(args)
|
||||||
mode = ns.assertmode
|
mode = ns.assertmode
|
||||||
no_assert = ns.noassert
|
|
||||||
no_magic = ns.nomagic
|
|
||||||
if no_assert or no_magic:
|
|
||||||
mode = "plain"
|
|
||||||
if mode == "rewrite":
|
if mode == "rewrite":
|
||||||
try:
|
try:
|
||||||
import ast # noqa
|
import ast # noqa
|
||||||
|
|
|
@ -125,7 +125,7 @@ class AssertionRewritingHook(object):
|
||||||
co = _read_pyc(fn_pypath, pyc, state.trace)
|
co = _read_pyc(fn_pypath, pyc, state.trace)
|
||||||
if co is None:
|
if co is None:
|
||||||
state.trace("rewriting %r" % (fn,))
|
state.trace("rewriting %r" % (fn,))
|
||||||
source_stat, co = _rewrite_test(state, fn_pypath)
|
source_stat, co = _rewrite_test(self.config, fn_pypath)
|
||||||
if co is None:
|
if co is None:
|
||||||
# Probably a SyntaxError in the test.
|
# Probably a SyntaxError in the test.
|
||||||
return None
|
return None
|
||||||
|
@ -252,8 +252,9 @@ N = "\n".encode("utf-8")
|
||||||
cookie_re = re.compile(r"^[ \t\f]*#.*coding[:=][ \t]*[-\w.]+")
|
cookie_re = re.compile(r"^[ \t\f]*#.*coding[:=][ \t]*[-\w.]+")
|
||||||
BOM_UTF8 = '\xef\xbb\xbf'
|
BOM_UTF8 = '\xef\xbb\xbf'
|
||||||
|
|
||||||
def _rewrite_test(state, fn):
|
def _rewrite_test(config, fn):
|
||||||
"""Try to read and rewrite *fn* and return the code object."""
|
"""Try to read and rewrite *fn* and return the code object."""
|
||||||
|
state = config._assertstate
|
||||||
try:
|
try:
|
||||||
stat = fn.stat()
|
stat = fn.stat()
|
||||||
source = fn.read("rb")
|
source = fn.read("rb")
|
||||||
|
@ -298,7 +299,7 @@ def _rewrite_test(state, fn):
|
||||||
# Let this pop up again in the real import.
|
# Let this pop up again in the real import.
|
||||||
state.trace("failed to parse: %r" % (fn,))
|
state.trace("failed to parse: %r" % (fn,))
|
||||||
return None, None
|
return None, None
|
||||||
rewrite_asserts(tree)
|
rewrite_asserts(tree, fn, config)
|
||||||
try:
|
try:
|
||||||
co = compile(tree, fn.strpath, "exec")
|
co = compile(tree, fn.strpath, "exec")
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
|
@ -354,9 +355,9 @@ def _read_pyc(source, pyc, trace=lambda x: None):
|
||||||
return co
|
return co
|
||||||
|
|
||||||
|
|
||||||
def rewrite_asserts(mod):
|
def rewrite_asserts(mod, module_path=None, config=None):
|
||||||
"""Rewrite the assert statements in mod."""
|
"""Rewrite the assert statements in mod."""
|
||||||
AssertionRewriter().run(mod)
|
AssertionRewriter(module_path, config).run(mod)
|
||||||
|
|
||||||
|
|
||||||
def _saferepr(obj):
|
def _saferepr(obj):
|
||||||
|
@ -543,6 +544,11 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(self, module_path, config):
|
||||||
|
super(AssertionRewriter, self).__init__()
|
||||||
|
self.module_path = module_path
|
||||||
|
self.config = config
|
||||||
|
|
||||||
def run(self, mod):
|
def run(self, mod):
|
||||||
"""Find all assert statements in *mod* and rewrite them."""
|
"""Find all assert statements in *mod* and rewrite them."""
|
||||||
if not mod.body:
|
if not mod.body:
|
||||||
|
@ -683,6 +689,10 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||||
the expression is false.
|
the expression is false.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
if isinstance(assert_.test, ast.Tuple) and self.config is not None:
|
||||||
|
fslocation = (self.module_path, assert_.lineno)
|
||||||
|
self.config.warn('R1', 'assertion is always true, perhaps '
|
||||||
|
'remove parentheses?', fslocation=fslocation)
|
||||||
self.statements = []
|
self.statements = []
|
||||||
self.variables = []
|
self.variables = []
|
||||||
self.variable_counter = itertools.count()
|
self.variable_counter = itertools.count()
|
||||||
|
|
|
@ -64,8 +64,9 @@ _preinit = []
|
||||||
|
|
||||||
default_plugins = (
|
default_plugins = (
|
||||||
"mark main terminal runner python pdb unittest capture skipping "
|
"mark main terminal runner python pdb unittest capture skipping "
|
||||||
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
|
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion "
|
||||||
"junitxml resultlog doctest cacheprovider setuponly setupplan").split()
|
"junitxml resultlog doctest cacheprovider freeze_support "
|
||||||
|
"setuponly setupplan").split()
|
||||||
|
|
||||||
builtin_plugins = set(default_plugins)
|
builtin_plugins = set(default_plugins)
|
||||||
builtin_plugins.add("pytester")
|
builtin_plugins.add("pytester")
|
||||||
|
@ -686,6 +687,10 @@ class OptionGroup:
|
||||||
results in help showing '--two-words' only, but --twowords gets
|
results in help showing '--two-words' only, but --twowords gets
|
||||||
accepted **and** the automatic destination is in args.twowords
|
accepted **and** the automatic destination is in args.twowords
|
||||||
"""
|
"""
|
||||||
|
conflict = set(optnames).intersection(
|
||||||
|
name for opt in self.options for name in opt.names())
|
||||||
|
if conflict:
|
||||||
|
raise ValueError("option names %s already added" % conflict)
|
||||||
option = Argument(*optnames, **attrs)
|
option = Argument(*optnames, **attrs)
|
||||||
self._addoption_instance(option, shortupper=False)
|
self._addoption_instance(option, shortupper=False)
|
||||||
|
|
||||||
|
@ -999,14 +1004,16 @@ class Config(object):
|
||||||
description, type, default = self._parser._inidict[name]
|
description, type, default = self._parser._inidict[name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ValueError("unknown configuration value: %r" %(name,))
|
raise ValueError("unknown configuration value: %r" %(name,))
|
||||||
try:
|
value = self._get_override_ini_value(name)
|
||||||
value = self.inicfg[name]
|
if value is None:
|
||||||
except KeyError:
|
try:
|
||||||
if default is not None:
|
value = self.inicfg[name]
|
||||||
return default
|
except KeyError:
|
||||||
if type is None:
|
if default is not None:
|
||||||
return ''
|
return default
|
||||||
return []
|
if type is None:
|
||||||
|
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 = []
|
l = []
|
||||||
|
@ -1037,6 +1044,20 @@ class Config(object):
|
||||||
l.append(relroot)
|
l.append(relroot)
|
||||||
return l
|
return l
|
||||||
|
|
||||||
|
def _get_override_ini_value(self, name):
|
||||||
|
value = None
|
||||||
|
# override_ini is a list of list, to support both -o foo1=bar1 foo2=bar2 and
|
||||||
|
# and -o foo1=bar1 -o foo2=bar2 options
|
||||||
|
# always use the last item if multiple value set for same ini-name,
|
||||||
|
# e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2
|
||||||
|
if self.getoption("override_ini", None):
|
||||||
|
for ini_config_list in self.option.override_ini:
|
||||||
|
for ini_config in ini_config_list:
|
||||||
|
(key, user_ini_value) = ini_config.split("=", 1)
|
||||||
|
if key == name:
|
||||||
|
value = user_ini_value
|
||||||
|
return value
|
||||||
|
|
||||||
def getoption(self, name, default=notset, skip=False):
|
def getoption(self, name, default=notset, skip=False):
|
||||||
""" return command line option value.
|
""" return command line option value.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
"""
|
||||||
|
Provides a function to report all internal modules for using freezing tools
|
||||||
|
pytest
|
||||||
|
"""
|
||||||
|
|
||||||
|
def pytest_namespace():
|
||||||
|
return {'freeze_includes': freeze_includes}
|
||||||
|
|
||||||
|
|
||||||
|
def freeze_includes():
|
||||||
|
"""
|
||||||
|
Returns a list of module names used by py.test that should be
|
||||||
|
included by cx_freeze.
|
||||||
|
"""
|
||||||
|
import py
|
||||||
|
import _pytest
|
||||||
|
result = list(_iter_all_modules(py))
|
||||||
|
result += list(_iter_all_modules(_pytest))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _iter_all_modules(package, prefix=''):
|
||||||
|
"""
|
||||||
|
Iterates over the names of all modules that can be found in the given
|
||||||
|
package, recursively.
|
||||||
|
Example:
|
||||||
|
_iter_all_modules(_pytest) ->
|
||||||
|
['_pytest.assertion.newinterpret',
|
||||||
|
'_pytest.capture',
|
||||||
|
'_pytest.core',
|
||||||
|
...
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import pkgutil
|
||||||
|
if type(package) is not str:
|
||||||
|
path, prefix = package.__path__[0], package.__name__ + '.'
|
||||||
|
else:
|
||||||
|
path = package
|
||||||
|
for _, name, is_package in pkgutil.iter_modules([path]):
|
||||||
|
if is_package:
|
||||||
|
for m in _iter_all_modules(os.path.join(path, name), prefix=name + '.'):
|
||||||
|
yield prefix + m
|
||||||
|
else:
|
||||||
|
yield prefix + name
|
|
@ -1,132 +0,0 @@
|
||||||
""" (deprecated) generate a single-file self-contained version of pytest """
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import pkgutil
|
|
||||||
|
|
||||||
import py
|
|
||||||
import _pytest
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def find_toplevel(name):
|
|
||||||
for syspath in sys.path:
|
|
||||||
base = py.path.local(syspath)
|
|
||||||
lib = base/name
|
|
||||||
if lib.check(dir=1):
|
|
||||||
return lib
|
|
||||||
mod = base.join("%s.py" % name)
|
|
||||||
if mod.check(file=1):
|
|
||||||
return mod
|
|
||||||
raise LookupError(name)
|
|
||||||
|
|
||||||
def pkgname(toplevel, rootpath, path):
|
|
||||||
parts = path.parts()[len(rootpath.parts()):]
|
|
||||||
return '.'.join([toplevel] + [x.purebasename for x in parts])
|
|
||||||
|
|
||||||
def pkg_to_mapping(name):
|
|
||||||
toplevel = find_toplevel(name)
|
|
||||||
name2src = {}
|
|
||||||
if toplevel.check(file=1): # module
|
|
||||||
name2src[toplevel.purebasename] = toplevel.read()
|
|
||||||
else: # package
|
|
||||||
for pyfile in toplevel.visit('*.py'):
|
|
||||||
pkg = pkgname(name, toplevel, pyfile)
|
|
||||||
name2src[pkg] = pyfile.read()
|
|
||||||
# with wheels py source code might be not be installed
|
|
||||||
# and the resulting genscript is useless, just bail out.
|
|
||||||
assert name2src, "no source code found for %r at %r" %(name, toplevel)
|
|
||||||
return name2src
|
|
||||||
|
|
||||||
def compress_mapping(mapping):
|
|
||||||
import base64, pickle, zlib
|
|
||||||
data = pickle.dumps(mapping, 2)
|
|
||||||
data = zlib.compress(data, 9)
|
|
||||||
data = base64.encodestring(data)
|
|
||||||
data = data.decode('ascii')
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def compress_packages(names):
|
|
||||||
mapping = {}
|
|
||||||
for name in names:
|
|
||||||
mapping.update(pkg_to_mapping(name))
|
|
||||||
return compress_mapping(mapping)
|
|
||||||
|
|
||||||
def generate_script(entry, packages):
|
|
||||||
data = compress_packages(packages)
|
|
||||||
tmpl = py.path.local(__file__).dirpath().join('standalonetemplate.py')
|
|
||||||
exe = tmpl.read()
|
|
||||||
exe = exe.replace('@SOURCES@', data)
|
|
||||||
exe = exe.replace('@ENTRY@', entry)
|
|
||||||
return exe
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
|
||||||
group = parser.getgroup("debugconfig")
|
|
||||||
group.addoption("--genscript", action="store", default=None,
|
|
||||||
dest="genscript", metavar="path",
|
|
||||||
help="create standalone pytest script at given target path.")
|
|
||||||
|
|
||||||
def pytest_cmdline_main(config):
|
|
||||||
import _pytest.config
|
|
||||||
genscript = config.getvalue("genscript")
|
|
||||||
if genscript:
|
|
||||||
tw = _pytest.config.create_terminal_writer(config)
|
|
||||||
tw.line("WARNING: usage of genscript is deprecated.",
|
|
||||||
red=True)
|
|
||||||
deps = ['py', '_pytest', 'pytest'] # pluggy is vendored
|
|
||||||
if sys.version_info < (2,7):
|
|
||||||
deps.append("argparse")
|
|
||||||
tw.line("generated script will run on python2.6-python3.3++")
|
|
||||||
else:
|
|
||||||
tw.line("WARNING: generated script will not run on python2.6 "
|
|
||||||
"due to 'argparse' dependency. Use python2.6 "
|
|
||||||
"to generate a python2.6 compatible script", red=True)
|
|
||||||
script = generate_script(
|
|
||||||
'import pytest; raise SystemExit(pytest.cmdline.main())',
|
|
||||||
deps,
|
|
||||||
)
|
|
||||||
genscript = py.path.local(genscript)
|
|
||||||
genscript.write(script)
|
|
||||||
tw.line("generated pytest standalone script: %s" % genscript,
|
|
||||||
bold=True)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_namespace():
|
|
||||||
return {'freeze_includes': freeze_includes}
|
|
||||||
|
|
||||||
|
|
||||||
def freeze_includes():
|
|
||||||
"""
|
|
||||||
Returns a list of module names used by pytest that should be
|
|
||||||
included by cx_freeze.
|
|
||||||
"""
|
|
||||||
result = list(_iter_all_modules(py))
|
|
||||||
result += list(_iter_all_modules(_pytest))
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def _iter_all_modules(package, prefix=''):
|
|
||||||
"""
|
|
||||||
Iterates over the names of all modules that can be found in the given
|
|
||||||
package, recursively.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
_iter_all_modules(_pytest) ->
|
|
||||||
['_pytest.assertion.newinterpret',
|
|
||||||
'_pytest.capture',
|
|
||||||
'_pytest.core',
|
|
||||||
...
|
|
||||||
]
|
|
||||||
"""
|
|
||||||
if type(package) is not str:
|
|
||||||
path, prefix = package.__path__[0], package.__name__ + '.'
|
|
||||||
else:
|
|
||||||
path = package
|
|
||||||
for _, name, is_package in pkgutil.iter_modules([path]):
|
|
||||||
if is_package:
|
|
||||||
for m in _iter_all_modules(os.path.join(path, name), prefix=name + '.'):
|
|
||||||
yield prefix + m
|
|
||||||
else:
|
|
||||||
yield prefix + name
|
|
|
@ -20,6 +20,10 @@ def pytest_addoption(parser):
|
||||||
group.addoption('--debug',
|
group.addoption('--debug',
|
||||||
action="store_true", dest="debug", default=False,
|
action="store_true", dest="debug", default=False,
|
||||||
help="store internal tracing debug information in 'pytestdebug.log'.")
|
help="store internal tracing debug information in 'pytestdebug.log'.")
|
||||||
|
# support for "--overwrite-ini ININAME=INIVALUE" to override values from the ini file
|
||||||
|
# Example '-o xfail_strict=True'.
|
||||||
|
group._addoption('-o', '--override-ini', nargs='*', dest="override_ini", action="append",
|
||||||
|
help="overrides ini values which do not have a separate command-line flag")
|
||||||
|
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
|
|
|
@ -656,6 +656,12 @@ class Module(pytest.File, PyCollector):
|
||||||
"Make sure your test modules/packages have valid Python names."
|
"Make sure your test modules/packages have valid Python names."
|
||||||
% (self.fspath, exc or exc_class)
|
% (self.fspath, exc or exc_class)
|
||||||
)
|
)
|
||||||
|
except _pytest.runner.Skipped:
|
||||||
|
raise self.CollectError(
|
||||||
|
"Using @pytest.skip outside a test (e.g. as a test function "
|
||||||
|
"decorator) is not allowed. Use @pytest.mark.skip or "
|
||||||
|
"@pytest.mark.skipif instead."
|
||||||
|
)
|
||||||
#print "imported test module", mod
|
#print "imported test module", mod
|
||||||
self.config.pluginmanager.consider_module(mod)
|
self.config.pluginmanager.consider_module(mod)
|
||||||
return mod
|
return mod
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
#! /usr/bin/env python
|
|
||||||
|
|
||||||
# Hi There!
|
|
||||||
# You may be wondering what this giant blob of binary data here is, you might
|
|
||||||
# even be worried that we're up to something nefarious (good for you for being
|
|
||||||
# paranoid!). This is a base64 encoding of a zip file, this zip file contains
|
|
||||||
# a fully functional basic pytest script.
|
|
||||||
#
|
|
||||||
# Pytest is a thing that tests packages, pytest itself is a package that some-
|
|
||||||
# one might want to install, especially if they're looking to run tests inside
|
|
||||||
# some package they want to install. Pytest has a lot of code to collect and
|
|
||||||
# execute tests, and other such sort of "tribal knowledge" that has been en-
|
|
||||||
# coded in its code base. Because of this we basically include a basic copy
|
|
||||||
# of pytest inside this blob. We do this because it let's you as a maintainer
|
|
||||||
# or application developer who wants people who don't deal with python much to
|
|
||||||
# easily run tests without installing the complete pytest package.
|
|
||||||
#
|
|
||||||
# If you're wondering how this is created: you can create it yourself if you
|
|
||||||
# have a complete pytest installation by using this command on the command-
|
|
||||||
# line: ``pytest --genscript=runtests.py``.
|
|
||||||
|
|
||||||
sources = """
|
|
||||||
@SOURCES@"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import base64
|
|
||||||
import zlib
|
|
||||||
|
|
||||||
class DictImporter(object):
|
|
||||||
def __init__(self, sources):
|
|
||||||
self.sources = sources
|
|
||||||
|
|
||||||
def find_module(self, fullname, path=None):
|
|
||||||
if fullname == "argparse" and sys.version_info >= (2,7):
|
|
||||||
# we were generated with <python2.7 (which pulls in argparse)
|
|
||||||
# but we are running now on a stdlib which has it, so use that.
|
|
||||||
return None
|
|
||||||
if fullname in self.sources:
|
|
||||||
return self
|
|
||||||
if fullname + '.__init__' in self.sources:
|
|
||||||
return self
|
|
||||||
return None
|
|
||||||
|
|
||||||
def load_module(self, fullname):
|
|
||||||
# print "load_module:", fullname
|
|
||||||
from types import ModuleType
|
|
||||||
try:
|
|
||||||
s = self.sources[fullname]
|
|
||||||
is_pkg = False
|
|
||||||
except KeyError:
|
|
||||||
s = self.sources[fullname + '.__init__']
|
|
||||||
is_pkg = True
|
|
||||||
|
|
||||||
co = compile(s, fullname, 'exec')
|
|
||||||
module = sys.modules.setdefault(fullname, ModuleType(fullname))
|
|
||||||
module.__file__ = "%s/%s" % (__file__, fullname)
|
|
||||||
module.__loader__ = self
|
|
||||||
if is_pkg:
|
|
||||||
module.__path__ = [fullname]
|
|
||||||
|
|
||||||
do_exec(co, module.__dict__) # noqa
|
|
||||||
return sys.modules[fullname]
|
|
||||||
|
|
||||||
def get_source(self, name):
|
|
||||||
res = self.sources.get(name)
|
|
||||||
if res is None:
|
|
||||||
res = self.sources.get(name + '.__init__')
|
|
||||||
return res
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
try:
|
|
||||||
import pkg_resources # noqa
|
|
||||||
except ImportError:
|
|
||||||
sys.stderr.write("ERROR: setuptools not installed\n")
|
|
||||||
sys.exit(2)
|
|
||||||
if sys.version_info >= (3, 0):
|
|
||||||
exec("def do_exec(co, loc): exec(co, loc)\n")
|
|
||||||
import pickle
|
|
||||||
sources = sources.encode("ascii") # ensure bytes
|
|
||||||
sources = pickle.loads(zlib.decompress(base64.decodebytes(sources)))
|
|
||||||
else:
|
|
||||||
import cPickle as pickle
|
|
||||||
exec("def do_exec(co, loc): exec co in loc\n")
|
|
||||||
sources = pickle.loads(zlib.decompress(base64.decodestring(sources)))
|
|
||||||
|
|
||||||
importer = DictImporter(sources)
|
|
||||||
sys.meta_path.insert(0, importer)
|
|
||||||
entry = "@ENTRY@"
|
|
||||||
do_exec(entry, locals()) # noqa
|
|
|
@ -27,9 +27,6 @@ def pytest_addoption(parser):
|
||||||
group._addoption('-l', '--showlocals',
|
group._addoption('-l', '--showlocals',
|
||||||
action="store_true", dest="showlocals", default=False,
|
action="store_true", dest="showlocals", default=False,
|
||||||
help="show locals in tracebacks (disabled by default).")
|
help="show locals in tracebacks (disabled by default).")
|
||||||
group._addoption('--report',
|
|
||||||
action="store", dest="report", default=None, metavar="opts",
|
|
||||||
help="(deprecated, use -r)")
|
|
||||||
group._addoption('--tb', metavar="style",
|
group._addoption('--tb', metavar="style",
|
||||||
action="store", dest="tbstyle", default='auto',
|
action="store", dest="tbstyle", default='auto',
|
||||||
choices=['auto', 'long', 'short', 'no', 'line', 'native'],
|
choices=['auto', 'long', 'short', 'no', 'line', 'native'],
|
||||||
|
@ -54,17 +51,6 @@ def pytest_configure(config):
|
||||||
|
|
||||||
def getreportopt(config):
|
def getreportopt(config):
|
||||||
reportopts = ""
|
reportopts = ""
|
||||||
optvalue = config.option.report
|
|
||||||
if optvalue:
|
|
||||||
py.builtin.print_("DEPRECATED: use -r instead of --report option.",
|
|
||||||
file=sys.stderr)
|
|
||||||
if optvalue:
|
|
||||||
for setting in optvalue.split(","):
|
|
||||||
setting = setting.strip()
|
|
||||||
if setting == "skipped":
|
|
||||||
reportopts += "s"
|
|
||||||
elif setting == "xfailed":
|
|
||||||
reportopts += "x"
|
|
||||||
reportchars = config.option.reportchars
|
reportchars = config.option.reportchars
|
||||||
if reportchars:
|
if reportchars:
|
||||||
for char in reportchars:
|
for char in reportchars:
|
||||||
|
|
|
@ -314,3 +314,6 @@ For further information, Benjamin Peterson wrote up `Behind the scenes of pytest
|
||||||
.. versionchanged:: 2.1
|
.. versionchanged:: 2.1
|
||||||
Introduce the ``--assert`` option. Deprecate ``--no-assert`` and
|
Introduce the ``--assert`` option. Deprecate ``--no-assert`` and
|
||||||
``--nomagic``.
|
``--nomagic``.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.0
|
||||||
|
Removes the ``--no-assert`` and``--nomagic`` options.
|
||||||
|
|
|
@ -193,45 +193,9 @@ Where to go next
|
||||||
Here are a few suggestions where to go next:
|
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, genscript support
|
* :ref:`good practices <goodpractices>` for virtualenv, test layout
|
||||||
* :ref:`fixtures` for providing a functional baseline to your tests
|
* :ref:`fixtures` for providing a functional baseline to your tests
|
||||||
* :ref:`apiref` for documentation and examples on using ``pytest``
|
* :ref:`apiref` for documentation and examples on using ``pytest``
|
||||||
* :ref:`plugins` managing and writing plugins
|
* :ref:`plugins` managing and writing plugins
|
||||||
|
|
||||||
.. _`installation issues`:
|
|
||||||
|
|
||||||
Known Installation issues
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
easy_install or pip not found?
|
|
||||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
.. _`install pip`: http://www.pip-installer.org/en/latest/index.html
|
|
||||||
|
|
||||||
`Install pip`_ for a state of the art python package installer.
|
|
||||||
|
|
||||||
Install `setuptools`_ to get ``easy_install`` which allows to install
|
|
||||||
``.egg`` binary format packages in addition to source-based ones.
|
|
||||||
|
|
||||||
pytest not found on Windows despite installation?
|
|
||||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
.. _`Python for Windows`: http://www.imladris.com/Scripts/PythonForWindows.html
|
|
||||||
|
|
||||||
- **Windows**: If "easy_install" or "pytest" are not found
|
|
||||||
you need to add the Python script path to your ``PATH``, see here:
|
|
||||||
`Python for Windows`_. You may alternatively use an `ActivePython install`_
|
|
||||||
which does this for you automatically.
|
|
||||||
|
|
||||||
.. _`ActivePython install`: http://www.activestate.com/activepython/downloads
|
|
||||||
|
|
||||||
.. _`Jython does not create command line launchers`: http://bugs.jython.org/issue1491
|
|
||||||
|
|
||||||
- **Jython2.5.1 on Windows XP**: `Jython does not create command line launchers`_
|
|
||||||
so ``pytest`` will not work correctly. You may install pytest on
|
|
||||||
CPython and type ``pytest --genscript=mytest`` and then use
|
|
||||||
``jython mytest`` to run your tests with Jython using ``pytest``.
|
|
||||||
|
|
||||||
:ref:`examples` for more complex examples
|
|
||||||
|
|
||||||
.. include:: links.inc
|
.. include:: links.inc
|
||||||
|
|
|
@ -243,38 +243,4 @@ using the ``--pytest-args`` or ``-a`` command-line option. For example::
|
||||||
is equivalent to running ``pytest --durations=5``.
|
is equivalent to running ``pytest --durations=5``.
|
||||||
|
|
||||||
|
|
||||||
.. _standalone:
|
|
||||||
.. _`genscript method`:
|
|
||||||
|
|
||||||
(deprecated) Create a pytest standalone script
|
|
||||||
-----------------------------------------------
|
|
||||||
|
|
||||||
.. deprecated:: 2.8
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
``genscript`` has been deprecated because:
|
|
||||||
|
|
||||||
* It cannot support plugins, rendering its usefulness extremely limited;
|
|
||||||
* Tooling has become much better since ``genscript`` was introduced;
|
|
||||||
* It is possible to build a zipped ``pytest`` application without the
|
|
||||||
shortcomings above.
|
|
||||||
|
|
||||||
There's no planned version in which this command will be removed
|
|
||||||
at the moment of this writing, but its use is discouraged for new
|
|
||||||
applications.
|
|
||||||
|
|
||||||
If you are a maintainer or application developer and want people
|
|
||||||
who don't deal with python much to easily run tests you may generate
|
|
||||||
a standalone ``pytest`` script::
|
|
||||||
|
|
||||||
pytest --genscript=runtests.py
|
|
||||||
|
|
||||||
This generates a ``runtests.py`` script which is a fully functional basic
|
|
||||||
``pytest`` script, running unchanged under Python2 and Python3.
|
|
||||||
You can tell people to download the script and then e.g. run it like this::
|
|
||||||
|
|
||||||
python runtests.py
|
|
||||||
|
|
||||||
|
|
||||||
.. include:: links.inc
|
.. include:: links.inc
|
||||||
|
|
|
@ -6,7 +6,7 @@ pytest: helps you write better programs
|
||||||
|
|
||||||
**a mature full-featured Python testing tool**
|
**a mature full-featured Python testing tool**
|
||||||
|
|
||||||
- runs on Posix/Windows, Python 2.6-3.5, PyPy and (possibly still) Jython-2.5.1
|
- runs on Posix/Windows, Python 2.6, 2.7 and 3.3-3.5, PyPy and (possibly still) Jython-2.5.1
|
||||||
- free and open source software, distributed under the terms of the :ref:`MIT license <license>`
|
- free and open source software, distributed under the terms of the :ref:`MIT license <license>`
|
||||||
- **well tested** with more than a thousand tests against itself
|
- **well tested** with more than a thousand tests against itself
|
||||||
- **strict backward compatibility policy** for safe pytest upgrades
|
- **strict backward compatibility policy** for safe pytest upgrades
|
||||||
|
@ -57,5 +57,3 @@ pytest: helps you write better programs
|
||||||
|
|
||||||
|
|
||||||
.. _`easy`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html
|
.. _`easy`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -138,7 +138,6 @@ in the `pytest repository <https://github.com/pytest-dev/pytest>`_.
|
||||||
_pytest.capture
|
_pytest.capture
|
||||||
_pytest.config
|
_pytest.config
|
||||||
_pytest.doctest
|
_pytest.doctest
|
||||||
_pytest.genscript
|
|
||||||
_pytest.helpconfig
|
_pytest.helpconfig
|
||||||
_pytest.junitxml
|
_pytest.junitxml
|
||||||
_pytest.mark
|
_pytest.mark
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
=========================
|
||||||
|
Parametrize with fixtures
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Problem
|
||||||
|
-------
|
||||||
|
|
||||||
|
As a user I have functional tests that I would like to run against various
|
||||||
|
scenarios.
|
||||||
|
|
||||||
|
In this particular example we want to generate a new project based on a
|
||||||
|
cookiecutter template. We want to test default values but also data that
|
||||||
|
emulates user input.
|
||||||
|
|
||||||
|
- use default values
|
||||||
|
|
||||||
|
- emulate user input
|
||||||
|
|
||||||
|
- specify 'author'
|
||||||
|
|
||||||
|
- specify 'project_slug'
|
||||||
|
|
||||||
|
- specify 'author' and 'project_slug'
|
||||||
|
|
||||||
|
This is how a functional test could look like:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def default_context():
|
||||||
|
return {'extra_context': {}}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(params=[
|
||||||
|
{'author': 'alice'},
|
||||||
|
{'project_slug': 'helloworld'},
|
||||||
|
{'author': 'bob', 'project_slug': 'foobar'},
|
||||||
|
])
|
||||||
|
def extra_context(request):
|
||||||
|
return {'extra_context': request.param}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(params=['default', 'extra'])
|
||||||
|
def context(request):
|
||||||
|
if request.param == 'default':
|
||||||
|
return request.getfuncargvalue('default_context')
|
||||||
|
else:
|
||||||
|
return request.getfuncargvalue('extra_context')
|
||||||
|
|
||||||
|
|
||||||
|
def test_generate_project(cookies, context):
|
||||||
|
"""Call the cookiecutter API to generate a new project from a
|
||||||
|
template.
|
||||||
|
"""
|
||||||
|
result = cookies.bake(extra_context=context)
|
||||||
|
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert result.exception is None
|
||||||
|
assert result.project.isdir()
|
||||||
|
|
||||||
|
|
||||||
|
Issues
|
||||||
|
------
|
||||||
|
|
||||||
|
* By using ``request.getfuncargvalue()`` we rely on actual fixture function
|
||||||
|
execution to know what fixtures are involved, due to it's dynamic nature
|
||||||
|
* More importantly, ``request.getfuncargvalue()`` cannot be combined with
|
||||||
|
parametrized fixtures, such as ``extra_context``
|
||||||
|
* This is very inconvenient if you wish to extend an existing test suite by
|
||||||
|
certain parameters for fixtures that are already used by tests
|
||||||
|
|
||||||
|
pytest version 3.0 reports an error if you try to run above code::
|
||||||
|
|
||||||
|
Failed: The requested fixture has no parameter defined for the current
|
||||||
|
test.
|
||||||
|
|
||||||
|
Requested fixture 'extra_context'
|
||||||
|
|
||||||
|
|
||||||
|
Proposed solution
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
A new function that can be used in modules can be used to dynamically define
|
||||||
|
fixtures from existing ones.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
pytest.define_combined_fixture(
|
||||||
|
name='context',
|
||||||
|
fixtures=['default_context', 'extra_context'],
|
||||||
|
)
|
||||||
|
|
||||||
|
The new fixture ``context`` inherits the scope from the used fixtures and yield
|
||||||
|
the following values.
|
||||||
|
|
||||||
|
- ``{}``
|
||||||
|
|
||||||
|
- ``{'author': 'alice'}``
|
||||||
|
|
||||||
|
- ``{'project_slug': 'helloworld'}``
|
||||||
|
|
||||||
|
- ``{'author': 'bob', 'project_slug': 'foobar'}``
|
||||||
|
|
||||||
|
Alternative approach
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
A new helper function named ``fixture_request`` tells pytest to yield all
|
||||||
|
parameters of a fixture.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@pytest.fixture(params=[
|
||||||
|
pytest.fixture_request('default_context'),
|
||||||
|
pytest.fixture_request('extra_context'),
|
||||||
|
])
|
||||||
|
def context(request):
|
||||||
|
"""Returns all values for ``default_context``, one-by-one before it
|
||||||
|
does the same for ``extra_context``.
|
||||||
|
|
||||||
|
request.param:
|
||||||
|
- {}
|
||||||
|
- {'author': 'alice'}
|
||||||
|
- {'project_slug': 'helloworld'}
|
||||||
|
- {'author': 'bob', 'project_slug': 'foobar'}
|
||||||
|
"""
|
||||||
|
return request.param
|
||||||
|
|
||||||
|
The same helper can be used in combination with ``pytest.mark.parametrize``.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'context, expected_response_code',
|
||||||
|
[
|
||||||
|
(pytest.fixture_request('default_context'), 0),
|
||||||
|
(pytest.fixture_request('extra_context'), 0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_generate_project(cookies, context, exit_code):
|
||||||
|
"""Call the cookiecutter API to generate a new project from a
|
||||||
|
template.
|
||||||
|
"""
|
||||||
|
result = cookies.bake(extra_context=context)
|
||||||
|
|
||||||
|
assert result.exit_code == exit_code
|
|
@ -1,28 +0,0 @@
|
||||||
|
|
||||||
(deprecated) generate standalone test script to be distributed along with an application.
|
|
||||||
============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:local:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
command line options
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
|
|
||||||
``--genscript=path``
|
|
||||||
create standalone ``pytest`` script at given target path.
|
|
||||||
|
|
||||||
Start improving this plugin in 30 seconds
|
|
||||||
=========================================
|
|
||||||
|
|
||||||
|
|
||||||
1. Download `pytest_genscript.py`_ plugin source code
|
|
||||||
2. put it somewhere as ``pytest_genscript.py`` into your import path
|
|
||||||
3. a subsequent ``pytest`` run will use your local version
|
|
||||||
|
|
||||||
Checkout customize_, other plugins_ or `get in contact`_.
|
|
||||||
|
|
||||||
.. include:: links.txt
|
|
|
@ -18,8 +18,6 @@ command line options
|
||||||
early-load given plugin (multi-allowed).
|
early-load given plugin (multi-allowed).
|
||||||
``--trace-config``
|
``--trace-config``
|
||||||
trace considerations of conftest.py files.
|
trace considerations of conftest.py files.
|
||||||
``--nomagic``
|
|
||||||
don't reinterpret asserts, no traceback cutting.
|
|
||||||
``--debug``
|
``--debug``
|
||||||
generate and show internal debugging information.
|
generate and show internal debugging information.
|
||||||
``--help-config``
|
``--help-config``
|
||||||
|
|
|
@ -2,10 +2,8 @@
|
||||||
.. _`pytest_recwarn.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_recwarn.py
|
.. _`pytest_recwarn.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_recwarn.py
|
||||||
.. _`unittest`: unittest.html
|
.. _`unittest`: unittest.html
|
||||||
.. _`pytest_monkeypatch.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_monkeypatch.py
|
.. _`pytest_monkeypatch.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_monkeypatch.py
|
||||||
.. _`pytest_genscript.py`: http://bitbucket.org/hpk42/py-trunk/raw/1.3.4/py/_plugin/pytest_genscript.py
|
|
||||||
.. _`pastebin`: pastebin.html
|
.. _`pastebin`: pastebin.html
|
||||||
.. _`skipping`: skipping.html
|
.. _`skipping`: skipping.html
|
||||||
.. _`genscript`: genscript.html
|
|
||||||
.. _`plugins`: index.html
|
.. _`plugins`: index.html
|
||||||
.. _`mark`: mark.html
|
.. _`mark`: mark.html
|
||||||
.. _`tmpdir`: tmpdir.html
|
.. _`tmpdir`: tmpdir.html
|
||||||
|
|
|
@ -18,8 +18,6 @@ command line options
|
||||||
show extra test summary info as specified by chars (f)ailed, (s)skipped, (x)failed, (X)passed.
|
show extra test summary info as specified by chars (f)ailed, (s)skipped, (x)failed, (X)passed.
|
||||||
``-l, --showlocals``
|
``-l, --showlocals``
|
||||||
show locals in tracebacks (disabled by default).
|
show locals in tracebacks (disabled by default).
|
||||||
``--report=opts``
|
|
||||||
(deprecated, use -r)
|
|
||||||
``--tb=style``
|
``--tb=style``
|
||||||
traceback print mode (long/short/line/no).
|
traceback print mode (long/short/line/no).
|
||||||
``--full-trace``
|
``--full-trace``
|
||||||
|
|
32
setup.py
32
setup.py
|
@ -13,7 +13,7 @@ classifiers = ['Development Status :: 6 - Mature',
|
||||||
'Topic :: Software Development :: Libraries',
|
'Topic :: Software Development :: Libraries',
|
||||||
'Topic :: Utilities'] + [
|
'Topic :: Utilities'] + [
|
||||||
('Programming Language :: Python :: %s' % x) for x in
|
('Programming Language :: Python :: %s' % x) for x in
|
||||||
'2 2.6 2.7 3 3.2 3.3 3.4 3.5'.split()]
|
'2 2.6 2.7 3 3.3 3.4 3.5'.split()]
|
||||||
|
|
||||||
with open('README.rst') as fd:
|
with open('README.rst') as fd:
|
||||||
long_description = fd.read()
|
long_description = fd.read()
|
||||||
|
@ -51,10 +51,10 @@ def main():
|
||||||
install_requires = ['py>=1.4.29'] # pluggy is vendored in _pytest.vendored_packages
|
install_requires = ['py>=1.4.29'] # pluggy is vendored in _pytest.vendored_packages
|
||||||
extras_require = {}
|
extras_require = {}
|
||||||
if has_environment_marker_support():
|
if has_environment_marker_support():
|
||||||
extras_require[':python_version=="2.6" or python_version=="3.0" or python_version=="3.1"'] = ['argparse']
|
extras_require[':python_version=="2.6"'] = ['argparse']
|
||||||
extras_require[':sys_platform=="win32"'] = ['colorama']
|
extras_require[':sys_platform=="win32"'] = ['colorama']
|
||||||
else:
|
else:
|
||||||
if sys.version_info < (2, 7) or (3,) <= sys.version_info < (3, 2):
|
if sys.version_info < (2, 7):
|
||||||
install_requires.append('argparse')
|
install_requires.append('argparse')
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
install_requires.append('colorama')
|
install_requires.append('colorama')
|
||||||
|
@ -69,7 +69,8 @@ def main():
|
||||||
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
||||||
author='Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others',
|
author='Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others',
|
||||||
author_email='holger at merlinux.eu',
|
author_email='holger at merlinux.eu',
|
||||||
entry_points=make_entry_points(),
|
entry_points={'console_scripts':
|
||||||
|
['pytest=pytest:main', 'py.test=pytest:main']},
|
||||||
classifiers=classifiers,
|
classifiers=classifiers,
|
||||||
cmdclass={'test': PyTest},
|
cmdclass={'test': PyTest},
|
||||||
# the following should be enabled for release
|
# the following should be enabled for release
|
||||||
|
@ -81,29 +82,6 @@ def main():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def cmdline_entrypoints(versioninfo, platform, basename):
|
|
||||||
target = 'pytest:main'
|
|
||||||
if platform.startswith('java'):
|
|
||||||
points = {'py.test-jython': target}
|
|
||||||
else:
|
|
||||||
if basename.startswith('pypy'):
|
|
||||||
points = {'py.test-%s' % basename: target}
|
|
||||||
else: # cpython
|
|
||||||
points = {'py.test-%s.%s' % versioninfo[:2] : target}
|
|
||||||
points['py.test'] = target
|
|
||||||
points['pytest'] = target
|
|
||||||
return points
|
|
||||||
|
|
||||||
|
|
||||||
def make_entry_points():
|
|
||||||
basename = os.path.basename(sys.executable)
|
|
||||||
points = cmdline_entrypoints(sys.version_info, sys.platform, basename)
|
|
||||||
keys = list(points.keys())
|
|
||||||
keys.sort()
|
|
||||||
l = ['%s = %s' % (x, points[x]) for x in keys]
|
|
||||||
return {'console_scripts': l}
|
|
||||||
|
|
||||||
|
|
||||||
class PyTest(Command):
|
class PyTest(Command):
|
||||||
user_options = []
|
user_options = []
|
||||||
def initialize_options(self):
|
def initialize_options(self):
|
||||||
|
|
|
@ -385,8 +385,7 @@ def test_deindent():
|
||||||
lines = deindent(source.splitlines())
|
lines = deindent(source.splitlines())
|
||||||
assert lines == ['', 'def f():', ' def g():', ' pass', ' ']
|
assert lines == ['', 'def f():', ' def g():', ' pass', ' ']
|
||||||
|
|
||||||
@pytest.mark.xfail("sys.version_info[:3] < (2,7,0) or "
|
@pytest.mark.xfail("sys.version_info[:3] < (2,7,0)")
|
||||||
"((3,0) <= sys.version_info[:2] < (3,2))")
|
|
||||||
def test_source_of_class_at_eof_without_newline(tmpdir):
|
def test_source_of_class_at_eof_without_newline(tmpdir):
|
||||||
# this test fails because the implicit inspect.getsource(A) below
|
# this test fails because the implicit inspect.getsource(A) below
|
||||||
# does not return the "x = 1" last line.
|
# does not return the "x = 1" last line.
|
||||||
|
@ -656,4 +655,3 @@ something
|
||||||
'''"""
|
'''"""
|
||||||
result = getstatement(1, source)
|
result = getstatement(1, source)
|
||||||
assert str(result) == "'''\n'''"
|
assert str(result) == "'''\n'''"
|
||||||
|
|
||||||
|
|
|
@ -474,16 +474,8 @@ def test_assertion_options(testdir):
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
assert "3 == 4" in result.stdout.str()
|
assert "3 == 4" in result.stdout.str()
|
||||||
off_options = (("--no-assert",),
|
result = testdir.runpytest_subprocess("--assert=plain")
|
||||||
("--nomagic",),
|
assert "3 == 4" not in result.stdout.str()
|
||||||
("--no-assert", "--nomagic"),
|
|
||||||
("--assert=plain",),
|
|
||||||
("--assert=plain", "--no-assert"),
|
|
||||||
("--assert=plain", "--nomagic"),
|
|
||||||
("--assert=plain", "--no-assert", "--nomagic"))
|
|
||||||
for opt in off_options:
|
|
||||||
result = testdir.runpytest_subprocess(*opt)
|
|
||||||
assert "3 == 4" not in result.stdout.str()
|
|
||||||
|
|
||||||
def test_old_assert_mode(testdir):
|
def test_old_assert_mode(testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
|
@ -559,7 +551,7 @@ def test_warn_missing(testdir):
|
||||||
result.stderr.fnmatch_lines([
|
result.stderr.fnmatch_lines([
|
||||||
"*WARNING*assert statements are not executed*",
|
"*WARNING*assert statements are not executed*",
|
||||||
])
|
])
|
||||||
result = testdir.run(sys.executable, "-OO", "-m", "pytest", "--no-assert")
|
result = testdir.run(sys.executable, "-OO", "-m", "pytest")
|
||||||
result.stderr.fnmatch_lines([
|
result.stderr.fnmatch_lines([
|
||||||
"*WARNING*assert statements are not executed*",
|
"*WARNING*assert statements are not executed*",
|
||||||
])
|
])
|
||||||
|
@ -640,3 +632,21 @@ def test_diff_newline_at_end(monkeypatch, testdir):
|
||||||
* + asdf
|
* + asdf
|
||||||
* ? +
|
* ? +
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
def test_assert_tuple_warning(testdir):
|
||||||
|
testdir.makepyfile("""
|
||||||
|
def test_tuple():
|
||||||
|
assert(False, 'you shall not pass')
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest('-rw')
|
||||||
|
result.stdout.fnmatch_lines('WR1*:2 assertion is always true*')
|
||||||
|
|
||||||
|
def test_assert_indirect_tuple_no_warning(testdir):
|
||||||
|
testdir.makepyfile("""
|
||||||
|
def test_tuple():
|
||||||
|
tpl = ('foo', 'bar')
|
||||||
|
assert tpl
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest('-rw')
|
||||||
|
output = '\n'.join(result.stdout.lines)
|
||||||
|
assert 'WR1' not in output
|
||||||
|
|
|
@ -365,7 +365,7 @@ def test_options_on_small_file_do_not_blow_up(testdir):
|
||||||
""")
|
""")
|
||||||
|
|
||||||
for opts in ([], ['-l'], ['-s'], ['--tb=no'], ['--tb=short'],
|
for opts in ([], ['-l'], ['-s'], ['--tb=no'], ['--tb=short'],
|
||||||
['--tb=long'], ['--fulltrace'], ['--nomagic'],
|
['--tb=long'], ['--fulltrace'],
|
||||||
['--traceconfig'], ['-v'], ['-v', '-v']):
|
['--traceconfig'], ['-v'], ['-v', '-v']):
|
||||||
runfiletest(opts + [path])
|
runfiletest(opts + [path])
|
||||||
|
|
||||||
|
@ -583,3 +583,92 @@ class TestRootdir:
|
||||||
inifile = tmpdir.ensure("pytest.ini")
|
inifile = tmpdir.ensure("pytest.ini")
|
||||||
rootdir, inifile, inicfg = determine_setup(inifile, [tmpdir])
|
rootdir, inifile, inicfg = determine_setup(inifile, [tmpdir])
|
||||||
assert rootdir == tmpdir
|
assert rootdir == tmpdir
|
||||||
|
|
||||||
|
class TestOverrideIniArgs:
|
||||||
|
""" test --override-ini """
|
||||||
|
@pytest.mark.parametrize("name", "setup.cfg tox.ini pytest.ini".split())
|
||||||
|
def test_override_ini_names(self, testdir, name):
|
||||||
|
testdir.tmpdir.join(name).write(py.std.textwrap.dedent("""
|
||||||
|
[pytest]
|
||||||
|
custom = 1.0
|
||||||
|
"""))
|
||||||
|
testdir.makeconftest("""
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
parser.addini("custom", "")
|
||||||
|
""")
|
||||||
|
testdir.makepyfile("""
|
||||||
|
def test_pass(pytestconfig):
|
||||||
|
ini_val = pytestconfig.getini("custom")
|
||||||
|
print('\\ncustom_option:%s\\n' % ini_val)
|
||||||
|
""")
|
||||||
|
|
||||||
|
result = testdir.runpytest("--override-ini", "custom=2.0", "-s")
|
||||||
|
assert result.ret == 0
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"custom_option:2.0"
|
||||||
|
])
|
||||||
|
|
||||||
|
result = testdir.runpytest("--override-ini", "custom=2.0",
|
||||||
|
"--override-ini=custom=3.0", "-s")
|
||||||
|
assert result.ret == 0
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"custom_option:3.0"
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def test_override_ini_pathlist(self, testdir):
|
||||||
|
testdir.makeconftest("""
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
parser.addini("paths", "my new ini value", type="pathlist")
|
||||||
|
""")
|
||||||
|
testdir.makeini("""
|
||||||
|
[pytest]
|
||||||
|
paths=blah.py
|
||||||
|
""")
|
||||||
|
testdir.makepyfile("""
|
||||||
|
import py.path
|
||||||
|
def test_pathlist(pytestconfig):
|
||||||
|
config_paths = pytestconfig.getini("paths")
|
||||||
|
print(config_paths)
|
||||||
|
for cpf in config_paths:
|
||||||
|
print('\\nuser_path:%s' % cpf.basename)
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest("--override-ini", 'paths=foo/bar1.py foo/bar2.py', "-s")
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"user_path:bar1.py",
|
||||||
|
"user_path:bar2.py"
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_override_multiple_and_default(self, testdir):
|
||||||
|
testdir.makeconftest("""
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
parser.addini("custom_option_1", "", default="o1")
|
||||||
|
parser.addini("custom_option_2", "", default="o2")
|
||||||
|
parser.addini("custom_option_3", "", default=False, type="bool")
|
||||||
|
parser.addini("custom_option_4", "", default=True, type="bool")
|
||||||
|
|
||||||
|
""")
|
||||||
|
testdir.makeini("""
|
||||||
|
[pytest]
|
||||||
|
custom_option_1=custom_option_1
|
||||||
|
custom_option_2=custom_option_2
|
||||||
|
""")
|
||||||
|
testdir.makepyfile("""
|
||||||
|
def test_multiple_options(pytestconfig):
|
||||||
|
prefix="custom_option"
|
||||||
|
for x in range(1,5):
|
||||||
|
ini_value=pytestconfig.getini("%s_%d" % (prefix, x))
|
||||||
|
print('\\nini%d:%s' % (x, ini_value))
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest("--override-ini",
|
||||||
|
'custom_option_1=fulldir=/tmp/user1',
|
||||||
|
'custom_option_2=url=/tmp/user2?a=b&d=e',
|
||||||
|
"-o", 'custom_option_3=True',
|
||||||
|
"-o", 'custom_option_4=no',
|
||||||
|
"-s")
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"ini1:fulldir=/tmp/user1",
|
||||||
|
"ini2:url=/tmp/user2?a=b&d=e",
|
||||||
|
"ini3:True",
|
||||||
|
"ini4:False"
|
||||||
|
])
|
|
@ -1,51 +0,0 @@
|
||||||
import pytest
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
|
||||||
def standalone(request):
|
|
||||||
return Standalone(request)
|
|
||||||
|
|
||||||
class Standalone:
|
|
||||||
def __init__(self, request):
|
|
||||||
self.testdir = request.getfuncargvalue("testdir")
|
|
||||||
script = "mypytest"
|
|
||||||
result = self.testdir.runpytest("--genscript=%s" % script)
|
|
||||||
assert result.ret == 0
|
|
||||||
self.script = self.testdir.tmpdir.join(script)
|
|
||||||
assert self.script.check()
|
|
||||||
|
|
||||||
def run(self, anypython, testdir, *args):
|
|
||||||
return testdir._run(anypython, self.script, *args)
|
|
||||||
|
|
||||||
def test_gen(testdir, anypython, standalone):
|
|
||||||
if sys.version_info >= (2,7):
|
|
||||||
result = testdir._run(anypython, "-c",
|
|
||||||
"import sys;print (sys.version_info >=(2,7))")
|
|
||||||
assert result.ret == 0
|
|
||||||
if result.stdout.str() == "False":
|
|
||||||
pytest.skip("genscript called from python2.7 cannot work "
|
|
||||||
"earlier python versions")
|
|
||||||
result = standalone.run(anypython, testdir, '--version')
|
|
||||||
if result.ret == 2:
|
|
||||||
result.stderr.fnmatch_lines(["*ERROR: setuptools not installed*"])
|
|
||||||
elif result.ret == 0:
|
|
||||||
result.stderr.fnmatch_lines([
|
|
||||||
"*imported from*mypytest*"
|
|
||||||
])
|
|
||||||
p = testdir.makepyfile("def test_func(): assert 0")
|
|
||||||
result = standalone.run(anypython, testdir, p)
|
|
||||||
assert result.ret != 0
|
|
||||||
else:
|
|
||||||
pytest.fail("Unexpected return code")
|
|
||||||
|
|
||||||
|
|
||||||
def test_freeze_includes():
|
|
||||||
"""
|
|
||||||
Smoke test for freeze_includes(), to ensure that it works across all
|
|
||||||
supported python versions.
|
|
||||||
"""
|
|
||||||
includes = pytest.freeze_includes()
|
|
||||||
assert len(includes) > 1
|
|
||||||
assert '_pytest.genscript' in includes
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from xml.dom import minidom
|
from xml.dom import minidom
|
||||||
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
|
||||||
import py
|
import py
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
@ -158,6 +157,47 @@ class TestPython:
|
||||||
snode = tnode.find_first_by_tag("skipped")
|
snode = tnode.find_first_by_tag("skipped")
|
||||||
snode.assert_attr(type="pytest.skip", message="hello23", )
|
snode.assert_attr(type="pytest.skip", message="hello23", )
|
||||||
|
|
||||||
|
def test_mark_skip_contains_name_reason(self, testdir):
|
||||||
|
testdir.makepyfile("""
|
||||||
|
import pytest
|
||||||
|
@pytest.mark.skip(reason="hello24")
|
||||||
|
def test_skip():
|
||||||
|
assert True
|
||||||
|
""")
|
||||||
|
result, dom = runandparse(testdir)
|
||||||
|
assert result.ret == 0
|
||||||
|
node = dom.find_first_by_tag("testsuite")
|
||||||
|
node.assert_attr(skips=1)
|
||||||
|
tnode = node.find_first_by_tag("testcase")
|
||||||
|
tnode.assert_attr(
|
||||||
|
file="test_mark_skip_contains_name_reason.py",
|
||||||
|
line="1",
|
||||||
|
classname="test_mark_skip_contains_name_reason",
|
||||||
|
name="test_skip")
|
||||||
|
snode = tnode.find_first_by_tag("skipped")
|
||||||
|
snode.assert_attr(type="pytest.skip", message="hello24", )
|
||||||
|
|
||||||
|
def test_mark_skipif_contains_name_reason(self, testdir):
|
||||||
|
testdir.makepyfile("""
|
||||||
|
import pytest
|
||||||
|
GLOBAL_CONDITION = True
|
||||||
|
@pytest.mark.skipif(GLOBAL_CONDITION, reason="hello25")
|
||||||
|
def test_skip():
|
||||||
|
assert True
|
||||||
|
""")
|
||||||
|
result, dom = runandparse(testdir)
|
||||||
|
assert result.ret == 0
|
||||||
|
node = dom.find_first_by_tag("testsuite")
|
||||||
|
node.assert_attr(skips=1)
|
||||||
|
tnode = node.find_first_by_tag("testcase")
|
||||||
|
tnode.assert_attr(
|
||||||
|
file="test_mark_skipif_contains_name_reason.py",
|
||||||
|
line="2",
|
||||||
|
classname="test_mark_skipif_contains_name_reason",
|
||||||
|
name="test_skip")
|
||||||
|
snode = tnode.find_first_by_tag("skipped")
|
||||||
|
snode.assert_attr(type="pytest.skip", message="hello25", )
|
||||||
|
|
||||||
def test_classname_instance(self, testdir):
|
def test_classname_instance(self, testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
class TestClass:
|
class TestClass:
|
||||||
|
@ -351,23 +391,6 @@ class TestPython:
|
||||||
fnode.assert_attr(message="collection failure")
|
fnode.assert_attr(message="collection failure")
|
||||||
assert "SyntaxError" in fnode.toxml()
|
assert "SyntaxError" in fnode.toxml()
|
||||||
|
|
||||||
def test_collect_skipped(self, testdir):
|
|
||||||
testdir.makepyfile("import pytest; pytest.skip('xyz')")
|
|
||||||
result, dom = runandparse(testdir)
|
|
||||||
assert result.ret == EXIT_NOTESTSCOLLECTED
|
|
||||||
node = dom.find_first_by_tag("testsuite")
|
|
||||||
node.assert_attr(skips=1, tests=1)
|
|
||||||
tnode = node.find_first_by_tag("testcase")
|
|
||||||
tnode.assert_attr(
|
|
||||||
file="test_collect_skipped.py",
|
|
||||||
name="test_collect_skipped")
|
|
||||||
|
|
||||||
# pytest doesn't give us a line here.
|
|
||||||
assert tnode["line"] is None
|
|
||||||
|
|
||||||
fnode = tnode.find_first_by_tag("skipped")
|
|
||||||
fnode.assert_attr(message="collection skipped")
|
|
||||||
|
|
||||||
def test_unicode(self, testdir):
|
def test_unicode(self, testdir):
|
||||||
value = 'hx\xc4\x85\xc4\x87\n'
|
value = 'hx\xc4\x85\xc4\x87\n'
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
|
|
|
@ -418,6 +418,32 @@ class TestFunctional:
|
||||||
items, rec = testdir.inline_genitems(p)
|
items, rec = testdir.inline_genitems(p)
|
||||||
self.assert_markers(items, test_foo=('a', 'b'), test_bar=('a',))
|
self.assert_markers(items, test_foo=('a', 'b'), test_bar=('a',))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.issue568
|
||||||
|
@pytest.mark.xfail(reason="markers smear on methods of base classes")
|
||||||
|
def test_mark_should_not_pass_to_siebling_class(self, testdir):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
class TestBase:
|
||||||
|
def test_foo(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@pytest.mark.b
|
||||||
|
class TestSub(TestBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestOtherSub(TestBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
""")
|
||||||
|
items, rec = testdir.inline_genitems(p)
|
||||||
|
base_item, sub_item, sub_item_other = items
|
||||||
|
assert not hasattr(base_item.obj, 'b')
|
||||||
|
assert not hasattr(sub_item_other.obj, 'b')
|
||||||
|
|
||||||
|
|
||||||
def test_mark_decorator_baseclasses_merged(self, testdir):
|
def test_mark_decorator_baseclasses_merged(self, testdir):
|
||||||
p = testdir.makepyfile("""
|
p = testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
|
@ -77,6 +77,13 @@ class TestParser:
|
||||||
assert len(group.options) == 1
|
assert len(group.options) == 1
|
||||||
assert isinstance(group.options[0], parseopt.Argument)
|
assert isinstance(group.options[0], parseopt.Argument)
|
||||||
|
|
||||||
|
def test_group_addoption_conflict(self):
|
||||||
|
group = parseopt.OptionGroup("hello again")
|
||||||
|
group.addoption("--option1", "--option-1", action="store_true")
|
||||||
|
with pytest.raises(ValueError) as err:
|
||||||
|
group.addoption("--option1", "--option-one", action="store_true")
|
||||||
|
assert str(set(["--option1"])) in str(err.value)
|
||||||
|
|
||||||
def test_group_shortopt_lowercase(self, parser):
|
def test_group_shortopt_lowercase(self, parser):
|
||||||
group = parser.getgroup("hello")
|
group = parser.getgroup("hello")
|
||||||
pytest.raises(ValueError, """
|
pytest.raises(ValueError, """
|
||||||
|
|
|
@ -83,19 +83,10 @@ class TestWithFunctionIntegration:
|
||||||
|
|
||||||
def test_collection_report(self, testdir):
|
def test_collection_report(self, testdir):
|
||||||
ok = testdir.makepyfile(test_collection_ok="")
|
ok = testdir.makepyfile(test_collection_ok="")
|
||||||
skip = testdir.makepyfile(test_collection_skip=
|
|
||||||
"import pytest ; pytest.skip('hello')")
|
|
||||||
fail = testdir.makepyfile(test_collection_fail="XXX")
|
fail = testdir.makepyfile(test_collection_fail="XXX")
|
||||||
lines = self.getresultlog(testdir, ok)
|
lines = self.getresultlog(testdir, ok)
|
||||||
assert not lines
|
assert not lines
|
||||||
|
|
||||||
lines = self.getresultlog(testdir, skip)
|
|
||||||
assert len(lines) == 2
|
|
||||||
assert lines[0].startswith("S ")
|
|
||||||
assert lines[0].endswith("test_collection_skip.py")
|
|
||||||
assert lines[1].startswith(" ")
|
|
||||||
assert lines[1].endswith("test_collection_skip.py:1: Skipped: hello")
|
|
||||||
|
|
||||||
lines = self.getresultlog(testdir, fail)
|
lines = self.getresultlog(testdir, fail)
|
||||||
assert lines
|
assert lines
|
||||||
assert lines[0].startswith("F ")
|
assert lines[0].startswith("F ")
|
||||||
|
|
|
@ -365,18 +365,6 @@ class TestSessionReports:
|
||||||
assert res[0].name == "test_func1"
|
assert res[0].name == "test_func1"
|
||||||
assert res[1].name == "TestClass"
|
assert res[1].name == "TestClass"
|
||||||
|
|
||||||
def test_skip_at_module_scope(self, testdir):
|
|
||||||
col = testdir.getmodulecol("""
|
|
||||||
import pytest
|
|
||||||
pytest.skip("hello")
|
|
||||||
def test_func():
|
|
||||||
pass
|
|
||||||
""")
|
|
||||||
rep = main.collect_one_node(col)
|
|
||||||
assert not rep.failed
|
|
||||||
assert not rep.passed
|
|
||||||
assert rep.skipped
|
|
||||||
|
|
||||||
|
|
||||||
reporttypes = [
|
reporttypes = [
|
||||||
runner.BaseReport,
|
runner.BaseReport,
|
||||||
|
|
|
@ -174,10 +174,6 @@ class TestNewSession(SessionTests):
|
||||||
class TestY(TestX):
|
class TestY(TestX):
|
||||||
pass
|
pass
|
||||||
""",
|
""",
|
||||||
test_two="""
|
|
||||||
import pytest
|
|
||||||
pytest.skip('xxx')
|
|
||||||
""",
|
|
||||||
test_three="xxxdsadsadsadsa",
|
test_three="xxxdsadsadsadsa",
|
||||||
__init__=""
|
__init__=""
|
||||||
)
|
)
|
||||||
|
@ -189,11 +185,9 @@ class TestNewSession(SessionTests):
|
||||||
started = reprec.getcalls("pytest_collectstart")
|
started = reprec.getcalls("pytest_collectstart")
|
||||||
finished = reprec.getreports("pytest_collectreport")
|
finished = reprec.getreports("pytest_collectreport")
|
||||||
assert len(started) == len(finished)
|
assert len(started) == len(finished)
|
||||||
assert len(started) == 8 # XXX extra TopCollector
|
assert len(started) == 7 # XXX extra TopCollector
|
||||||
colfail = [x for x in finished if x.failed]
|
colfail = [x for x in finished if x.failed]
|
||||||
colskipped = [x for x in finished if x.skipped]
|
|
||||||
assert len(colfail) == 1
|
assert len(colfail) == 1
|
||||||
assert len(colskipped) == 1
|
|
||||||
|
|
||||||
def test_minus_x_import_error(self, testdir):
|
def test_minus_x_import_error(self, testdir):
|
||||||
testdir.makepyfile(__init__="")
|
testdir.makepyfile(__init__="")
|
||||||
|
|
|
@ -209,7 +209,7 @@ class TestXFail:
|
||||||
def test_this_false():
|
def test_this_false():
|
||||||
assert 1
|
assert 1
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest(p, '--report=xfailed', )
|
result = testdir.runpytest(p, '-rx', )
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*test_one*test_this*",
|
"*test_one*test_this*",
|
||||||
"*NOTRUN*noway",
|
"*NOTRUN*noway",
|
||||||
|
@ -227,7 +227,7 @@ class TestXFail:
|
||||||
def setup_module(mod):
|
def setup_module(mod):
|
||||||
raise ValueError(42)
|
raise ValueError(42)
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest(p, '--report=xfailed', )
|
result = testdir.runpytest(p, '-rx', )
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*test_one*test_this*",
|
"*test_one*test_this*",
|
||||||
"*NOTRUN*hello",
|
"*NOTRUN*hello",
|
||||||
|
@ -682,19 +682,15 @@ def test_skipped_reasons_functional(testdir):
|
||||||
def test_method(self):
|
def test_method(self):
|
||||||
doskip()
|
doskip()
|
||||||
""",
|
""",
|
||||||
test_two = """
|
|
||||||
from conftest import doskip
|
|
||||||
doskip()
|
|
||||||
""",
|
|
||||||
conftest = """
|
conftest = """
|
||||||
import pytest
|
import pytest
|
||||||
def doskip():
|
def doskip():
|
||||||
pytest.skip('test')
|
pytest.skip('test')
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = testdir.runpytest('--report=skipped')
|
result = testdir.runpytest('-rs')
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*SKIP*3*conftest.py:3: test",
|
"*SKIP*2*conftest.py:3: test",
|
||||||
])
|
])
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
|
@ -941,3 +937,19 @@ def test_xfail_item(testdir):
|
||||||
assert not failed
|
assert not failed
|
||||||
xfailed = [r for r in skipped if hasattr(r, 'wasxfail')]
|
xfailed = [r for r in skipped if hasattr(r, 'wasxfail')]
|
||||||
assert xfailed
|
assert xfailed
|
||||||
|
|
||||||
|
|
||||||
|
def test_module_level_skip_error(testdir):
|
||||||
|
"""
|
||||||
|
Verify that using pytest.skip at module level causes a collection error
|
||||||
|
"""
|
||||||
|
testdir.makepyfile("""
|
||||||
|
import pytest
|
||||||
|
@pytest.skip
|
||||||
|
def test_func():
|
||||||
|
assert True
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines(
|
||||||
|
"*Using @pytest.skip outside a test * is not allowed*"
|
||||||
|
)
|
||||||
|
|
|
@ -223,8 +223,7 @@ class TestCollectonly:
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest("--collect-only", "-rs")
|
result = testdir.runpytest("--collect-only", "-rs")
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"SKIP*hello*",
|
"*ERROR collecting*",
|
||||||
"*1 skip*",
|
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_collectonly_failed_module(self, testdir):
|
def test_collectonly_failed_module(self, testdir):
|
||||||
|
@ -591,16 +590,7 @@ def test_getreportopt():
|
||||||
class config:
|
class config:
|
||||||
class option:
|
class option:
|
||||||
reportchars = ""
|
reportchars = ""
|
||||||
config.option.report = "xfailed"
|
|
||||||
assert getreportopt(config) == "x"
|
|
||||||
|
|
||||||
config.option.report = "xfailed,skipped"
|
|
||||||
assert getreportopt(config) == "xs"
|
|
||||||
|
|
||||||
config.option.report = "skipped,xfailed"
|
|
||||||
assert getreportopt(config) == "sx"
|
|
||||||
|
|
||||||
config.option.report = "skipped"
|
|
||||||
config.option.reportchars = "sf"
|
config.option.reportchars = "sf"
|
||||||
assert getreportopt(config) == "sf"
|
assert getreportopt(config) == "sf"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue