Merge remote-tracking branch 'upstream/master' into features
This commit is contained in:
commit
654af0ba25
3
AUTHORS
3
AUTHORS
|
@ -78,6 +78,7 @@ Kale Kundert
|
|||
Katarzyna Jachim
|
||||
Kevin Cox
|
||||
Lee Kamentsky
|
||||
Lev Maximov
|
||||
Lukas Bednar
|
||||
Maciek Fijalkowski
|
||||
Maho
|
||||
|
@ -107,6 +108,7 @@ Punyashloka Biswal
|
|||
Quentin Pradet
|
||||
Ralf Schmitt
|
||||
Raphael Pierzina
|
||||
Raquel Alegre
|
||||
Roberto Polli
|
||||
Romain Dorgueil
|
||||
Roman Bolshakov
|
||||
|
@ -126,6 +128,7 @@ Ted Xiao
|
|||
Thomas Grainger
|
||||
Tom Viner
|
||||
Trevor Bekolay
|
||||
Tyler Goodlet
|
||||
Vasily Kuznetsov
|
||||
Wouter van Ackooy
|
||||
Xuecong Liao
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
New Features
|
||||
------------
|
||||
|
||||
|
||||
*
|
||||
|
||||
*
|
||||
|
@ -14,8 +13,8 @@ New Features
|
|||
Changes
|
||||
-------
|
||||
|
||||
* Testcase reports with a url attribute will now properly write this to junitxml.
|
||||
Thanks `@fushi`_ for the PR
|
||||
* Testcase reports with a ``url`` attribute will now properly write this to junitxml.
|
||||
Thanks `@fushi`_ for the PR (`#1874`_).
|
||||
|
||||
* Remove common items from dict comparision output when verbosity=1. Also update
|
||||
the truncation message to make it clearer that pytest truncates all
|
||||
|
@ -35,9 +34,44 @@ Changes
|
|||
.. _@mattduck: https://github.com/mattduck
|
||||
|
||||
.. _#1512: https://github.com/pytest-dev/pytest/issues/1512
|
||||
.. _#1874: https://github.com/pytest-dev/pytest/pull/1874
|
||||
.. _#1952: https://github.com/pytest-dev/pytest/pull/1952
|
||||
|
||||
|
||||
3.0.3.dev
|
||||
=========
|
||||
|
||||
* The ``ids`` argument to ``parametrize`` again accepts ``unicode`` strings
|
||||
in Python 2 (`#1905`_).
|
||||
Thanks `@philpep`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
* Assertions are now being rewritten for plugins in development mode
|
||||
(``pip install -e``) (`#1934`_).
|
||||
Thanks `@nicoddemus`_ for the PR.
|
||||
|
||||
* Fix pkg_resources import error in Jython projects (`#1853`_).
|
||||
Thanks `@raquel-ucl`_ for the PR.
|
||||
|
||||
* Got rid of ``AttributeError: 'Module' object has no attribute '_obj'`` exception
|
||||
in Python 3 (`#1944`_).
|
||||
Thanks `@axil`_ for the PR.
|
||||
|
||||
* Explain a bad scope value passed to ``@fixture`` declarations or
|
||||
a ``MetaFunc.parametrize()`` call. Thanks `@tgoodlet`_ for the PR.
|
||||
|
||||
|
||||
.. _@philpep: https://github.com/philpep
|
||||
.. _@raquel-ucl: https://github.com/raquel-ucl
|
||||
.. _@axil: https://github.com/axil
|
||||
.. _@tgoodlet: https://github.com/tgoodlet
|
||||
|
||||
.. _#1853: https://github.com/pytest-dev/pytest/issues/1853
|
||||
.. _#1905: https://github.com/pytest-dev/pytest/issues/1905
|
||||
.. _#1934: https://github.com/pytest-dev/pytest/issues/1934
|
||||
.. _#1944: https://github.com/pytest-dev/pytest/issues/1944
|
||||
|
||||
|
||||
|
||||
3.0.2
|
||||
=====
|
||||
|
||||
|
@ -126,7 +160,11 @@ time or change existing behaviors in order to make them less surprising/more use
|
|||
|
||||
* Reinterpretation mode has now been removed. Only plain and rewrite
|
||||
mode are available, consequently the ``--assert=reinterp`` option is
|
||||
no longer available. Thanks `@flub`_ for the PR.
|
||||
no longer available. This also means files imported from plugins or
|
||||
``conftest.py`` will not benefit from improved assertions by
|
||||
default, you should use ``pytest.register_assert_rewrite()`` to
|
||||
explicitly turn on assertion rewriting for those files. Thanks
|
||||
`@flub`_ for the PR.
|
||||
|
||||
* The following deprecated commandline options were removed:
|
||||
|
||||
|
|
|
@ -54,8 +54,8 @@ Note: this assumes you have already registered on pypi.
|
|||
11. Send release announcement to mailing lists:
|
||||
|
||||
- pytest-dev@python.org
|
||||
- testing-in-python@lists.idyll.org
|
||||
- python-announce-list@python.org
|
||||
- testing-in-python@lists.idyll.org (only for minor/major releases)
|
||||
|
||||
And announce the release on Twitter, making sure to add the hashtag ``#pytest``.
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ if sys.version_info[0] >= 3:
|
|||
else:
|
||||
from ._py2traceback import format_exception_only
|
||||
|
||||
|
||||
class Code(object):
|
||||
""" wrapper around Python code objects """
|
||||
def __init__(self, rawcode):
|
||||
|
@ -28,6 +29,8 @@ class Code(object):
|
|||
def __eq__(self, other):
|
||||
return self.raw == other.raw
|
||||
|
||||
__hash__ = None
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
|
|
|
@ -52,22 +52,21 @@ class Source(object):
|
|||
return str(self) == other
|
||||
return False
|
||||
|
||||
__hash__ = None
|
||||
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, int):
|
||||
return self.lines[key]
|
||||
else:
|
||||
if key.step not in (None, 1):
|
||||
raise IndexError("cannot slice a Source with a step")
|
||||
return self.__getslice__(key.start, key.stop)
|
||||
newsource = Source()
|
||||
newsource.lines = self.lines[key.start:key.stop]
|
||||
return newsource
|
||||
|
||||
def __len__(self):
|
||||
return len(self.lines)
|
||||
|
||||
def __getslice__(self, start, end):
|
||||
newsource = Source()
|
||||
newsource.lines = self.lines[start:end]
|
||||
return newsource
|
||||
|
||||
def strip(self):
|
||||
""" return new source object with trailing
|
||||
and leading blank lines removed.
|
||||
|
|
|
@ -105,7 +105,7 @@ except NameError:
|
|||
def assertrepr_compare(config, op, left, right):
|
||||
"""Return specialised explanations for some operators/operands"""
|
||||
width = 80 - 15 - len(op) - 2 # 15 chars indentation, 1 space around op
|
||||
left_repr = py.io.saferepr(left, maxsize=int(width/2))
|
||||
left_repr = py.io.saferepr(left, maxsize=int(width//2))
|
||||
right_repr = py.io.saferepr(right, maxsize=width-len(left_repr))
|
||||
|
||||
summary = u('%s %s %s') % (ecu(left_repr), op, ecu(right_repr))
|
||||
|
|
|
@ -5,7 +5,6 @@ import traceback
|
|||
import types
|
||||
import warnings
|
||||
|
||||
import pkg_resources
|
||||
import py
|
||||
# DON't import pytest here because it causes import cycle troubles
|
||||
import sys, os
|
||||
|
@ -952,18 +951,24 @@ class Config(object):
|
|||
except SystemError:
|
||||
mode = 'plain'
|
||||
else:
|
||||
import pkg_resources
|
||||
self.pluginmanager.rewrite_hook = hook
|
||||
for entrypoint in pkg_resources.iter_entry_points('pytest11'):
|
||||
for entry in entrypoint.dist._get_metadata('RECORD'):
|
||||
fn = entry.split(',')[0]
|
||||
is_simple_module = os.sep not in fn and fn.endswith('.py')
|
||||
is_package = fn.count(os.sep) == 1 and fn.endswith('__init__.py')
|
||||
if is_simple_module:
|
||||
module_name, ext = os.path.splitext(fn)
|
||||
hook.mark_rewrite(module_name)
|
||||
elif is_package:
|
||||
package_name = os.path.dirname(fn)
|
||||
hook.mark_rewrite(package_name)
|
||||
# 'RECORD' available for plugins installed normally (pip install)
|
||||
# 'SOURCES.txt' available for plugins installed in dev mode (pip install -e)
|
||||
# for installed plugins 'SOURCES.txt' returns an empty list, and vice-versa
|
||||
# so it shouldn't be an issue
|
||||
for metadata in ('RECORD', 'SOURCES.txt'):
|
||||
for entry in entrypoint.dist._get_metadata(metadata):
|
||||
fn = entry.split(',')[0]
|
||||
is_simple_module = os.sep not in fn and fn.endswith('.py')
|
||||
is_package = fn.count(os.sep) == 1 and fn.endswith('__init__.py')
|
||||
if is_simple_module:
|
||||
module_name, ext = os.path.splitext(fn)
|
||||
hook.mark_rewrite(module_name)
|
||||
elif is_package:
|
||||
package_name = os.path.dirname(fn)
|
||||
hook.mark_rewrite(package_name)
|
||||
self._warn_about_missing_assertion(mode)
|
||||
|
||||
def _warn_about_missing_assertion(self, mode):
|
||||
|
|
|
@ -599,12 +599,29 @@ class ScopeMismatchError(Exception):
|
|||
which has a lower scope (e.g. a Session one calls a function one)
|
||||
"""
|
||||
|
||||
|
||||
scopes = "session module class function".split()
|
||||
scopenum_function = scopes.index("function")
|
||||
|
||||
|
||||
def scopemismatch(currentscope, newscope):
|
||||
return scopes.index(newscope) > scopes.index(currentscope)
|
||||
|
||||
|
||||
def scope2index(scope, descr, where=None):
|
||||
"""Look up the index of ``scope`` and raise a descriptive value error
|
||||
if not defined.
|
||||
"""
|
||||
try:
|
||||
return scopes.index(scope)
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
"{0} {1}has an unsupported scope value '{2}'".format(
|
||||
descr, 'from {0} '.format(where) if where else '',
|
||||
scope)
|
||||
)
|
||||
|
||||
|
||||
class FixtureLookupError(LookupError):
|
||||
""" could not return a requested Fixture (missing or invalid). """
|
||||
def __init__(self, argname, request, msg=None):
|
||||
|
@ -703,6 +720,7 @@ def call_fixture_func(fixturefunc, request, kwargs):
|
|||
res = fixturefunc(**kwargs)
|
||||
return res
|
||||
|
||||
|
||||
class FixtureDef:
|
||||
""" A container for a factory definition. """
|
||||
def __init__(self, fixturemanager, baseid, argname, func, scope, params,
|
||||
|
@ -713,7 +731,11 @@ class FixtureDef:
|
|||
self.func = func
|
||||
self.argname = argname
|
||||
self.scope = scope
|
||||
self.scopenum = scopes.index(scope or "function")
|
||||
self.scopenum = scope2index(
|
||||
scope or "function",
|
||||
descr='fixture {0}'.format(func.__name__),
|
||||
where=baseid
|
||||
)
|
||||
self.params = params
|
||||
startindex = unittest and 1 or None
|
||||
self.argnames = getfuncargnames(func, startindex=startindex)
|
||||
|
|
|
@ -25,7 +25,7 @@ def pytest_addoption(parser):
|
|||
help="only run tests which match the given substring expression. "
|
||||
"An expression is a python evaluatable expression "
|
||||
"where all names are substring-matched against test names "
|
||||
"and their parent classes. Example: -k 'test_method or test "
|
||||
"and their parent classes. Example: -k 'test_method or test_"
|
||||
"other' matches all test functions and classes whose name "
|
||||
"contains 'test_method' or 'test_other'. "
|
||||
"Additionally keywords are matched to classes and functions "
|
||||
|
|
|
@ -205,11 +205,10 @@ class PyobjContext(object):
|
|||
class PyobjMixin(PyobjContext):
|
||||
def obj():
|
||||
def fget(self):
|
||||
try:
|
||||
return self._obj
|
||||
except AttributeError:
|
||||
obj = getattr(self, '_obj', None)
|
||||
if obj is None:
|
||||
self._obj = obj = self._getobj()
|
||||
return obj
|
||||
return obj
|
||||
def fset(self, value):
|
||||
self._obj = value
|
||||
return property(fget, fset, None, "underlying python object")
|
||||
|
@ -772,7 +771,7 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
|
|||
It will also override any fixture-function defined scope, allowing
|
||||
to set a dynamic scope using test context or configuration.
|
||||
"""
|
||||
from _pytest.fixtures import scopes
|
||||
from _pytest.fixtures import scope2index
|
||||
from _pytest.mark import extract_argvalue
|
||||
from py.io import saferepr
|
||||
|
||||
|
@ -801,7 +800,8 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
|
|||
if scope is None:
|
||||
scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect)
|
||||
|
||||
scopenum = scopes.index(scope)
|
||||
scopenum = scope2index(
|
||||
scope, descr='call to {0}'.format(self.parametrize))
|
||||
valtypes = {}
|
||||
for arg in argnames:
|
||||
if arg not in self.fixturenames:
|
||||
|
@ -833,7 +833,7 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
|
|||
raise ValueError('%d tests specified with %d ids' %(
|
||||
len(argvalues), len(ids)))
|
||||
for id_value in ids:
|
||||
if id_value is not None and not isinstance(id_value, str):
|
||||
if id_value is not None and not isinstance(id_value, py.builtin._basestring):
|
||||
msg = 'ids must be list of strings, found: %s (type: %s)'
|
||||
raise ValueError(msg % (saferepr(id_value), type(id_value).__name__))
|
||||
ids = idmaker(argnames, argvalues, idfn, ids, self.config)
|
||||
|
@ -1352,6 +1352,8 @@ class approx(object):
|
|||
return False
|
||||
return all(a == x for a, x in zip(actual, self.expected))
|
||||
|
||||
__hash__ = None
|
||||
|
||||
def __ne__(self, actual):
|
||||
return not (actual == self)
|
||||
|
||||
|
@ -1431,6 +1433,8 @@ class ApproxNonIterable(object):
|
|||
# Return true if the two numbers are within the tolerance.
|
||||
return abs(self.expected - actual) <= self.tolerance
|
||||
|
||||
__hash__ = None
|
||||
|
||||
def __ne__(self, actual):
|
||||
return not (actual == self)
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@ environment:
|
|||
matrix:
|
||||
# create multiple jobs to execute a set of tox runs on each; this is to workaround having
|
||||
# builds timing out in AppVeyor
|
||||
- TOXENV: "linting,py26,py27,py33,py34,py35,pypy"
|
||||
# pypy is disabled until #1963 gets fixed
|
||||
- TOXENV: "linting,py26,py27,py33,py34,py35"
|
||||
- TOXENV: "py27-pexpect,py27-xdist,py27-trial,py35-pexpect,py35-xdist,py35-trial"
|
||||
- TOXENV: "py27-nobyte,doctesting,freeze,docs"
|
||||
|
||||
|
|
|
@ -19,10 +19,9 @@ REGENDOC_ARGS := \
|
|||
--normalize "@/tmp/pytest-of-.*/pytest-\d+@PYTEST_TMPDIR@" \
|
||||
|
||||
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
|
||||
|
||||
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
|
@ -36,22 +35,6 @@ help:
|
|||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
SITETARGET=$(shell ./_getdoctarget.py)
|
||||
|
||||
showtarget:
|
||||
@echo $(SITETARGET)
|
||||
|
||||
install: html
|
||||
# for access talk to someone with login rights to
|
||||
# pytest-dev@pytest.org to add your ssh key
|
||||
rsync -avz _build/html/ pytest-dev@pytest.org:pytest.org/$(SITETARGET)
|
||||
|
||||
installpdf: latexpdf
|
||||
@scp $(BUILDDIR)/latex/pytest.pdf pytest-dev@pytest.org:pytest.org/$(SITETARGET)
|
||||
|
||||
installall: clean install installpdf
|
||||
@echo "done"
|
||||
|
||||
regen:
|
||||
PYTHONDONTWRITEBYTECODE=1 COLUMNS=76 regendoc --update *.rst */*.rst ${REGENDOC_ARGS}
|
||||
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import py
|
||||
|
||||
def get_version_string():
|
||||
fn = py.path.local(__file__).join("..", "..", "..",
|
||||
"_pytest", "__init__.py")
|
||||
for line in fn.readlines():
|
||||
if "version" in line and not line.strip().startswith('#'):
|
||||
return eval(line.split("=")[-1])
|
||||
|
||||
def get_minor_version_string():
|
||||
return ".".join(get_version_string().split(".")[:2])
|
||||
|
||||
if __name__ == "__main__":
|
||||
print (get_minor_version_string())
|
|
@ -6,6 +6,6 @@
|
|||
<li><a href="https://github.com/pytest-dev/pytest/">pytest @ GitHub</a></li>
|
||||
<li><a href="http://plugincompat.herokuapp.com/">3rd party plugins</a></li>
|
||||
<li><a href="https://github.com/pytest-dev/pytest/issues">Issue Tracker</a></li>
|
||||
<li><a href="http://pytest.org/latest/pytest.pdf">PDF Documentation</a>
|
||||
<li><a href="https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf">PDF Documentation</a>
|
||||
</ul>
|
||||
|
||||
|
|
|
@ -19,11 +19,8 @@
|
|||
# The short X.Y version.
|
||||
|
||||
import os, sys
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
import _getdoctarget
|
||||
|
||||
version = _getdoctarget.get_minor_version_string()
|
||||
release = _getdoctarget.get_version_string()
|
||||
from _pytest import __version__ as version
|
||||
release = ".".join(version.split(".")[:2])
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
|
|
|
@ -59,6 +59,7 @@ Method reference of the monkeypatch fixture
|
|||
-------------------------------------------
|
||||
|
||||
.. autoclass:: MonkeyPatch
|
||||
:members:
|
||||
|
||||
``monkeypatch.setattr/delattr/delitem/delenv()`` all
|
||||
by default raise an Exception if the target does not exist.
|
||||
|
|
|
@ -216,6 +216,3 @@ The **metafunc** object
|
|||
.. currentmodule:: _pytest.python
|
||||
.. autoclass:: Metafunc
|
||||
:members:
|
||||
|
||||
.. automethod:: Metafunc.parametrize
|
||||
.. automethod:: Metafunc.addcall(funcargs=None,id=_notexists,param=_notexists)
|
||||
|
|
|
@ -293,6 +293,20 @@ imperatively, in test or setup code::
|
|||
# or
|
||||
pytest.skip("unsupported configuration")
|
||||
|
||||
Note that calling ``pytest.skip`` at the module level
|
||||
is not allowed since pytest 3.0. If you are upgrading
|
||||
and ``pytest.skip`` was being used at the module level, you can set a
|
||||
``pytestmark`` variable:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# before pytest 3.0
|
||||
pytest.skip('skipping all tests because of reasons')
|
||||
# after pytest 3.0
|
||||
pytestmark = pytest.mark.skip('skipping all tests because of reasons')
|
||||
|
||||
``pytestmark`` applies a mark or list of marks to all tests in a module.
|
||||
|
||||
|
||||
Skipping on a missing import dependency
|
||||
--------------------------------------------------
|
||||
|
@ -371,3 +385,27 @@ The equivalent with "boolean conditions" is::
|
|||
imported before pytest's argument parsing takes place. For example,
|
||||
``conftest.py`` files are imported before command line parsing and thus
|
||||
``config.getvalue()`` will not execute correctly.
|
||||
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
Here's a quick guide on how to skip tests in a module in different situations:
|
||||
|
||||
1. Skip all tests in a module unconditionally:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pytestmark = pytest.mark.skip('all tests still WIP')
|
||||
|
||||
2. Skip all tests in a module based on some condition:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pytestmark = pytest.mark.skipif(sys.platform == 'win32', 'tests for linux only')
|
||||
|
||||
3. Skip all tests in a module if some import is missing:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pexpect = pytest.importorskip('pexpect')
|
||||
|
|
|
@ -38,7 +38,11 @@ the general ``pytest`` documentation for many more examples.
|
|||
Running tests from ``unittest.TestCase`` subclasses with ``--pdb`` will
|
||||
disable tearDown and cleanup methods for the case that an Exception
|
||||
occurs. This allows proper post mortem debugging for all applications
|
||||
which have significant logic in their tearDown machinery.
|
||||
which have significant logic in their tearDown machinery. However,
|
||||
supporting this feature has the following side effect: If people
|
||||
overwrite ``unittest.TestCase`` ``__call__`` or ``run``, they need to
|
||||
to overwrite ``debug`` in the same way (this is also true for standard
|
||||
unittest).
|
||||
|
||||
Mixing pytest fixtures into unittest.TestCase style tests
|
||||
-----------------------------------------------------------
|
||||
|
|
|
@ -1063,6 +1063,22 @@ class TestFixtureUsages:
|
|||
"*1 error*"
|
||||
])
|
||||
|
||||
def test_invalid_scope(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
@pytest.fixture(scope="functions")
|
||||
def badscope():
|
||||
pass
|
||||
|
||||
def test_nothing(badscope):
|
||||
pass
|
||||
""")
|
||||
result = testdir.runpytest_inprocess()
|
||||
result.stdout.fnmatch_lines(
|
||||
("*ValueError: fixture badscope from test_invalid_scope.py has an unsupported"
|
||||
" scope value 'functions'")
|
||||
)
|
||||
|
||||
def test_funcarg_parametrized_and_used_twice(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
|
|
|
@ -96,6 +96,14 @@ class TestMetafunc:
|
|||
pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5,6]))
|
||||
pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5,6]))
|
||||
|
||||
def test_parametrize_bad_scope(self, testdir):
|
||||
def func(x): pass
|
||||
metafunc = self.Metafunc(func)
|
||||
try:
|
||||
metafunc.parametrize("x", [1], scope='doggy')
|
||||
except ValueError as ve:
|
||||
assert "has an unsupported scope value 'doggy'" in str(ve)
|
||||
|
||||
def test_parametrize_and_id(self):
|
||||
def func(x, y): pass
|
||||
metafunc = self.Metafunc(func)
|
||||
|
@ -105,6 +113,14 @@ class TestMetafunc:
|
|||
ids = [x.id for x in metafunc._calls]
|
||||
assert ids == ["basic-abc", "basic-def", "advanced-abc", "advanced-def"]
|
||||
|
||||
def test_parametrize_and_id_unicode(self):
|
||||
"""Allow unicode strings for "ids" parameter in Python 2 (##1905)"""
|
||||
def func(x): pass
|
||||
metafunc = self.Metafunc(func)
|
||||
metafunc.parametrize("x", [1, 2], ids=[u'basic', u'advanced'])
|
||||
ids = [x.id for x in metafunc._calls]
|
||||
assert ids == [u"basic", u"advanced"]
|
||||
|
||||
def test_parametrize_with_wrong_number_of_ids(self, testdir):
|
||||
def func(x, y): pass
|
||||
metafunc = self.Metafunc(func)
|
||||
|
|
|
@ -108,7 +108,8 @@ class TestImportHookInstallation:
|
|||
assert result.ret == 0
|
||||
|
||||
@pytest.mark.parametrize('mode', ['plain', 'rewrite'])
|
||||
def test_installed_plugin_rewrite(self, testdir, mode):
|
||||
@pytest.mark.parametrize('plugin_state', ['development', 'installed'])
|
||||
def test_installed_plugin_rewrite(self, testdir, mode, plugin_state):
|
||||
# Make sure the hook is installed early enough so that plugins
|
||||
# installed via setuptools are re-written.
|
||||
testdir.tmpdir.join('hampkg').ensure(dir=1)
|
||||
|
@ -135,13 +136,22 @@ class TestImportHookInstallation:
|
|||
'mainwrapper.py': """
|
||||
import pytest, pkg_resources
|
||||
|
||||
plugin_state = "{plugin_state}"
|
||||
|
||||
class DummyDistInfo:
|
||||
project_name = 'spam'
|
||||
version = '1.0'
|
||||
|
||||
def _get_metadata(self, name):
|
||||
return ['spamplugin.py,sha256=abc,123',
|
||||
'hampkg/__init__.py,sha256=abc,123']
|
||||
# 'RECORD' meta-data only available in installed plugins
|
||||
if name == 'RECORD' and plugin_state == "installed":
|
||||
return ['spamplugin.py,sha256=abc,123',
|
||||
'hampkg/__init__.py,sha256=abc,123']
|
||||
# 'SOURCES.txt' meta-data only available for plugins in development mode
|
||||
elif name == 'SOURCES.txt' and plugin_state == "development":
|
||||
return ['spamplugin.py',
|
||||
'hampkg/__init__.py']
|
||||
return []
|
||||
|
||||
class DummyEntryPoint:
|
||||
name = 'spam'
|
||||
|
@ -159,7 +169,7 @@ class TestImportHookInstallation:
|
|||
|
||||
pkg_resources.iter_entry_points = iter_entry_points
|
||||
pytest.main()
|
||||
""",
|
||||
""".format(plugin_state=plugin_state),
|
||||
'test_foo.py': """
|
||||
def test(check_first):
|
||||
check_first([10, 30], 30)
|
||||
|
@ -862,3 +872,15 @@ def test_assert_with_unicode(monkeypatch, testdir):
|
|||
""")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(['*AssertionError*'])
|
||||
|
||||
def test_issue_1944(testdir):
|
||||
testdir.makepyfile("""
|
||||
def f():
|
||||
return
|
||||
|
||||
assert f() == 10
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(["*1 error*"])
|
||||
assert "AttributeError: 'Module' object has no attribute '_obj'" not in result.stdout.str()
|
||||
|
||||
|
|
11
tox.ini
11
tox.ini
|
@ -10,8 +10,8 @@ envlist=
|
|||
[testenv]
|
||||
commands= pytest --lsof -rfsxX {posargs:testing}
|
||||
passenv = USER USERNAME
|
||||
deps=
|
||||
hypothesis
|
||||
deps=
|
||||
hypothesis>=3.5.2
|
||||
nose
|
||||
mock
|
||||
requests
|
||||
|
@ -47,7 +47,7 @@ commands = flake8 pytest.py _pytest testing
|
|||
deps=pytest-xdist>=1.13
|
||||
mock
|
||||
nose
|
||||
hypothesis
|
||||
hypothesis>=3.5.2
|
||||
commands=
|
||||
pytest -n1 -rfsxX {posargs:testing}
|
||||
|
||||
|
@ -71,8 +71,9 @@ commands=
|
|||
pytest -rfsxX test_pdb.py test_terminal.py test_unittest.py
|
||||
|
||||
[testenv:py27-nobyte]
|
||||
deps=pytest-xdist>=1.13
|
||||
hypothesis
|
||||
deps=
|
||||
pytest-xdist>=1.13
|
||||
hypothesis>=3.5.2
|
||||
distribute=true
|
||||
setenv=
|
||||
PYTHONDONTWRITEBYTECODE=1
|
||||
|
|
Loading…
Reference in New Issue