Merge pull request #4100 from nicoddemus/merge-master-into-features

Merge master into features
This commit is contained in:
Anthony Sottile 2018-10-10 07:35:09 -07:00 committed by GitHub
commit 943bbdd8ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 176 additions and 162 deletions

View File

@ -7,13 +7,13 @@ repos:
args: [--safe, --quiet] args: [--safe, --quiet]
language_version: python3 language_version: python3
- repo: https://github.com/asottile/blacken-docs - repo: https://github.com/asottile/blacken-docs
rev: v0.2.0 rev: v0.3.0
hooks: hooks:
- id: blacken-docs - id: blacken-docs
additional_dependencies: [black==18.6b4] additional_dependencies: [black==18.9b0]
language_version: python3 language_version: python3
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v1.3.0 rev: v1.4.0-1
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
@ -22,11 +22,12 @@ repos:
exclude: _pytest/debugging.py exclude: _pytest/debugging.py
- id: flake8 - id: flake8
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v1.2.0 rev: v1.8.0
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--keep-percent-format]
- repo: https://github.com/pre-commit/pygrep-hooks - repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.0.0 rev: v1.1.0
hooks: hooks:
- id: rst-backticks - id: rst-backticks
- repo: local - repo: local

View File

@ -1,5 +1,3 @@
# 10000 iterations, just for relative comparison # 10000 iterations, just for relative comparison
# 2.7.5 3.3.2 # 2.7.5 3.3.2
# FilesCompleter 75.1109 69.2116 # FilesCompleter 75.1109 69.2116

View File

@ -1,4 +1,3 @@
import pytest import pytest

View File

@ -0,0 +1 @@
Improve error message when test functions of ``unittest.TestCase`` subclasses use a parametrized fixture.

View File

@ -0,0 +1 @@
``request.fixturenames`` now correctly returns the name of fixtures created by ``request.getfixturevalue()``.

1
changelog/4058.doc.rst Normal file
View File

@ -0,0 +1 @@
Update fixture documentation to specify that a fixture can be invoked twice in the scope it's defined for.

1
changelog/4064.doc.rst Normal file
View File

@ -0,0 +1 @@
According to unittest.rst, setUpModule and tearDownModule were not implemented, but it turns out they are. So updated the documentation for unittest.

View File

@ -0,0 +1 @@
Fix source reindenting by using ``textwrap.dedent`` directly.

View File

@ -20,8 +20,7 @@ Thanks to all who contributed to this release, among them:
* Ondřej Súkup * Ondřej Súkup
* Ronny Pfannschmidt * Ronny Pfannschmidt
* T.E.A de Souza * T.E.A de Souza
* Victor * Victor Maryama
* victor
Happy testing, Happy testing,

View File

@ -22,10 +22,9 @@ Thanks to all who contributed to this release, among them:
* Ronny Pfannschmidt * Ronny Pfannschmidt
* Sankt Petersbug * Sankt Petersbug
* Tyler Richard * Tyler Richard
* Victor * Victor Maryama
* Vlad Shcherbina * Vlad Shcherbina
* turturica * turturica
* victor
* wim glenn * wim glenn

View File

@ -245,9 +245,9 @@ class TestCustomAssertMsg(object):
a = 1 a = 1
b = 2 b = 2
assert ( assert A.a == b, (
A.a == b "A.a appears not to be b\n" "or does not appear to be b\none of those"
), "A.a appears not to be b\n" "or does not appear to be b\none of those" )
def test_custom_repr(self): def test_custom_repr(self):
class JSON(object): class JSON(object):

View File

@ -1,4 +1,3 @@
hello = "world" hello = "world"

View File

@ -1,4 +1,3 @@
import py import py
failure_demo = py.path.local(__file__).dirpath("failure_demo.py") failure_demo = py.path.local(__file__).dirpath("failure_demo.py")

View File

@ -1,4 +1,3 @@
import pytest import pytest

View File

@ -852,6 +852,8 @@ In that order.
can be changed between releases (even bug fixes) so it shouldn't be relied on for scripting can be changed between releases (even bug fixes) so it shouldn't be relied on for scripting
or automation. or automation.
.. _freezing-pytest:
Freezing pytest Freezing pytest
--------------- ---------------

View File

@ -259,6 +259,11 @@ instance, you can simply declare it:
Finally, the ``class`` scope will invoke the fixture once per test *class*. Finally, the ``class`` scope will invoke the fixture once per test *class*.
.. note::
Pytest will only cache one instance of a fixture at a time.
This means that when using a parametrized fixture, pytest may invoke a fixture more than once in the given scope.
``package`` scope (experimental) ``package`` scope (experimental)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -69,17 +69,15 @@ You may also discover more plugins through a `pytest- pypi.python.org search`_.
Requiring/Loading plugins in a test module or conftest file Requiring/Loading plugins in a test module or conftest file
----------------------------------------------------------- -----------------------------------------------------------
You can require plugins in a test module or a conftest file like this:: You can require plugins in a test module or a conftest file like this:
pytest_plugins = "myapp.testsupport.myplugin", .. code-block:: python
pytest_plugins = ("myapp.testsupport.myplugin",)
When the test module or conftest plugin is loaded the specified plugins When the test module or conftest plugin is loaded the specified plugins
will be loaded as well. will be loaded as well.
pytest_plugins = "myapp.testsupport.myplugin"
which will import the specified module as a ``pytest`` plugin.
.. note:: .. note::
Requiring plugins using a ``pytest_plugins`` variable in non-root Requiring plugins using a ``pytest_plugins`` variable in non-root
``conftest.py`` files is deprecated. See ``conftest.py`` files is deprecated. See

View File

@ -84,6 +84,12 @@ pytest.warns
.. autofunction:: pytest.warns(expected_warning: Exception, [match]) .. autofunction:: pytest.warns(expected_warning: Exception, [match])
:with: :with:
pytest.freeze_includes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Tutorial**: :ref:`freezing-pytest`.
.. autofunction:: pytest.freeze_includes
.. _`marks ref`: .. _`marks ref`:

View File

@ -22,16 +22,15 @@ Almost all ``unittest`` features are supported:
* ``@unittest.skip`` style decorators; * ``@unittest.skip`` style decorators;
* ``setUp/tearDown``; * ``setUp/tearDown``;
* ``setUpClass/tearDownClass()``; * ``setUpClass/tearDownClass``;
* ``setUpModule/tearDownModule``;
.. _`load_tests protocol`: https://docs.python.org/3/library/unittest.html#load-tests-protocol .. _`load_tests protocol`: https://docs.python.org/3/library/unittest.html#load-tests-protocol
.. _`setUpModule/tearDownModule`: https://docs.python.org/3/library/unittest.html#setupmodule-and-teardownmodule
.. _`subtests`: https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests .. _`subtests`: https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests
Up to this point pytest does not have support for the following features: Up to this point pytest does not have support for the following features:
* `load_tests protocol`_; * `load_tests protocol`_;
* `setUpModule/tearDownModule`_;
* `subtests`_; * `subtests`_;
Benefits out of the box Benefits out of the box

View File

@ -1,4 +1,3 @@
"""allow bash-completion for argparse with argcomplete if installed """allow bash-completion for argparse with argcomplete if installed
needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail
to find the magic string, so _ARGCOMPLETE env. var is never set, and to find the magic string, so _ARGCOMPLETE env. var is never set, and

View File

@ -7,6 +7,7 @@ import linecache
import sys import sys
import six import six
import inspect import inspect
import textwrap
import tokenize import tokenize
import py import py
@ -23,7 +24,6 @@ class Source(object):
def __init__(self, *parts, **kwargs): def __init__(self, *parts, **kwargs):
self.lines = lines = [] self.lines = lines = []
de = kwargs.get("deindent", True) de = kwargs.get("deindent", True)
rstrip = kwargs.get("rstrip", True)
for part in parts: for part in parts:
if not part: if not part:
partlines = [] partlines = []
@ -33,11 +33,6 @@ class Source(object):
partlines = [x.rstrip("\n") for x in part] partlines = [x.rstrip("\n") for x in part]
elif isinstance(part, six.string_types): elif isinstance(part, six.string_types):
partlines = part.split("\n") partlines = part.split("\n")
if rstrip:
while partlines:
if partlines[-1].strip():
break
partlines.pop()
else: else:
partlines = getsource(part, deindent=de).lines partlines = getsource(part, deindent=de).lines
if de: if de:
@ -115,17 +110,10 @@ class Source(object):
ast, start, end = getstatementrange_ast(lineno, self) ast, start, end = getstatementrange_ast(lineno, self)
return start, end return start, end
def deindent(self, offset=None): def deindent(self):
""" return a new source object deindented by offset. """return a new source object deindented."""
If offset is None then guess an indentation offset from
the first non-blank line. Subsequent lines which have a
lower indentation offset will be copied verbatim as
they are assumed to be part of multilines.
"""
# XXX maybe use the tokenizer to properly handle multiline
# strings etc.pp?
newsource = Source() newsource = Source()
newsource.lines[:] = deindent(self.lines, offset) newsource.lines[:] = deindent(self.lines)
return newsource return newsource
def isparseable(self, deindent=True): def isparseable(self, deindent=True):
@ -268,47 +256,8 @@ def getsource(obj, **kwargs):
return Source(strsrc, **kwargs) return Source(strsrc, **kwargs)
def deindent(lines, offset=None): def deindent(lines):
if offset is None: return textwrap.dedent("\n".join(lines)).splitlines()
for line in lines:
line = line.expandtabs()
s = line.lstrip()
if s:
offset = len(line) - len(s)
break
else:
offset = 0
if offset == 0:
return list(lines)
newlines = []
def readline_generator(lines):
for line in lines:
yield line + "\n"
it = readline_generator(lines)
try:
for _, _, (sline, _), (eline, _), _ in tokenize.generate_tokens(
lambda: next(it)
):
if sline > len(lines):
break # End of input reached
if sline > len(newlines):
line = lines[sline - 1].expandtabs()
if line.lstrip() and line[:offset].isspace():
line = line[offset:] # Deindent
newlines.append(line)
for i in range(sline, eline):
# Don't deindent continuing lines of
# multiline tokens (i.e. multiline strings)
newlines.append(lines[i])
except (IndentationError, tokenize.TokenError):
pass
# Add any lines we didn't see. E.g. if an exception was raised.
newlines.extend(lines[len(newlines) :])
return newlines
def get_statement_startend2(lineno, node): def get_statement_startend2(lineno, node):

View File

@ -359,8 +359,10 @@ class FixtureRequest(FuncargnamesCompatAttr):
@property @property
def fixturenames(self): def fixturenames(self):
# backward incompatible note: now a readonly property """names of all active fixtures in this request"""
return list(self._pyfuncitem._fixtureinfo.names_closure) result = list(self._pyfuncitem._fixtureinfo.names_closure)
result.extend(set(self._fixture_defs).difference(result))
return result
@property @property
def node(self): def node(self):
@ -565,7 +567,20 @@ class FixtureRequest(FuncargnamesCompatAttr):
except (AttributeError, ValueError): except (AttributeError, ValueError):
param = NOTSET param = NOTSET
param_index = 0 param_index = 0
if fixturedef.params is not None: has_params = fixturedef.params is not None
fixtures_not_supported = getattr(funcitem, "nofuncargs", False)
if has_params and fixtures_not_supported:
msg = (
"{name} does not support fixtures, maybe unittest.TestCase subclass?\n"
"Node id: {nodeid}\n"
"Function type: {typename}"
).format(
name=funcitem.name,
nodeid=funcitem.nodeid,
typename=type(funcitem).__name__,
)
fail(msg)
if has_params:
frame = inspect.stack()[3] frame = inspect.stack()[3]
frameinfo = inspect.getframeinfo(frame[0]) frameinfo = inspect.getframeinfo(frame[0])
source_path = frameinfo.filename source_path = frameinfo.filename
@ -574,9 +589,11 @@ class FixtureRequest(FuncargnamesCompatAttr):
if source_path.relto(funcitem.config.rootdir): if source_path.relto(funcitem.config.rootdir):
source_path = source_path.relto(funcitem.config.rootdir) source_path = source_path.relto(funcitem.config.rootdir)
msg = ( msg = (
"The requested fixture has no parameter defined for the " "The requested fixture has no parameter defined for test:\n"
"current test.\n\nRequested fixture '{}' defined in:\n{}" " {}\n\n"
"Requested fixture '{}' defined in:\n{}"
"\n\nRequested here:\n{}:{}".format( "\n\nRequested here:\n{}:{}".format(
funcitem.nodeid,
fixturedef.argname, fixturedef.argname,
getlocation(fixturedef.func, funcitem.config.rootdir), getlocation(fixturedef.func, funcitem.config.rootdir),
source_path, source_path,

View File

@ -38,7 +38,7 @@ class Junit(py.xml.Namespace):
# this dynamically instead of hardcoding it. The spec range of valid # this dynamically instead of hardcoding it. The spec range of valid
# chars is: Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] # chars is: Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
# | [#x10000-#x10FFFF] # | [#x10000-#x10FFFF]
_legal_chars = (0x09, 0x0A, 0x0d) _legal_chars = (0x09, 0x0A, 0x0D)
_legal_ranges = ((0x20, 0x7E), (0x80, 0xD7FF), (0xE000, 0xFFFD), (0x10000, 0x10FFFF)) _legal_ranges = ((0x20, 0x7E), (0x80, 0xD7FF), (0xE000, 0xFFFD), (0x10000, 0x10FFFF))
_legal_xml_re = [ _legal_xml_re = [
unicode("%s-%s") % (unichr(low), unichr(high)) unicode("%s-%s") % (unichr(low), unichr(high))

View File

@ -213,7 +213,8 @@ class LogCaptureFixture(object):
def __init__(self, item): def __init__(self, item):
"""Creates a new funcarg.""" """Creates a new funcarg."""
self._item = item self._item = item
self._initial_log_levels = {} # type: Dict[str, int] # dict of log name -> log level # dict of log name -> log level
self._initial_log_levels = {} # type: Dict[str, int]
def _finalize(self): def _finalize(self):
"""Finalizes the fixture. """Finalizes the fixture.

View File

@ -90,7 +90,10 @@ class MarkEvaluator(object):
else: else:
if "reason" not in mark.kwargs: if "reason" not in mark.kwargs:
# XXX better be checked at collection time # XXX better be checked at collection time
msg = "you need to specify reason=STRING " "when using booleans as conditions." msg = (
"you need to specify reason=STRING "
"when using booleans as conditions."
)
fail(msg) fail(msg)
result = bool(expr) result = bool(expr)
if result: if result:

View File

@ -31,6 +31,14 @@ failsonjython = pytest.mark.xfail("sys.platform.startswith('java')")
pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3])) pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3]))
@pytest.fixture
def limited_recursion_depth():
before = sys.getrecursionlimit()
sys.setrecursionlimit(150)
yield
sys.setrecursionlimit(before)
class TWMock(object): class TWMock(object):
WRITE = object() WRITE = object()
@ -239,7 +247,7 @@ class TestTraceback_f_g_h(object):
raise RuntimeError("hello") raise RuntimeError("hello")
f(n - 1) f(n - 1)
excinfo = pytest.raises(RuntimeError, f, 100) excinfo = pytest.raises(RuntimeError, f, 25)
monkeypatch.delattr(excinfo.traceback.__class__, "recursionindex") monkeypatch.delattr(excinfo.traceback.__class__, "recursionindex")
repr = excinfo.getrepr() repr = excinfo.getrepr()
assert "RuntimeError: hello" in str(repr.reprcrash) assert "RuntimeError: hello" in str(repr.reprcrash)
@ -1341,11 +1349,13 @@ def test_cwd_deleted(testdir):
assert "INTERNALERROR" not in result.stdout.str() + result.stderr.str() assert "INTERNALERROR" not in result.stdout.str() + result.stderr.str()
@pytest.mark.usefixtures("limited_recursion_depth")
def test_exception_repr_extraction_error_on_recursion(): def test_exception_repr_extraction_error_on_recursion():
""" """
Ensure we can properly detect a recursion error even Ensure we can properly detect a recursion error even
if some locals raise error on comparison (#2459). if some locals raise error on comparison (#2459).
""" """
from _pytest.pytester import LineMatcher
class numpy_like(object): class numpy_like(object):
def __eq__(self, other): def __eq__(self, other):
@ -1361,40 +1371,30 @@ def test_exception_repr_extraction_error_on_recursion():
def b(x): def b(x):
return a(numpy_like()) return a(numpy_like())
try: with pytest.raises(RuntimeError) as excinfo:
a(numpy_like()) a(numpy_like())
except: # noqa
from _pytest._code.code import ExceptionInfo
from _pytest.pytester import LineMatcher
exc_info = ExceptionInfo() matcher = LineMatcher(str(excinfo.getrepr()).splitlines())
matcher.fnmatch_lines(
matcher = LineMatcher(str(exc_info.getrepr()).splitlines()) [
matcher.fnmatch_lines( "!!! Recursion error detected, but an error occurred locating the origin of recursion.",
[ "*The following exception happened*",
"!!! Recursion error detected, but an error occurred locating the origin of recursion.", "*ValueError: The truth value of an array*",
"*The following exception happened*", ]
"*ValueError: The truth value of an array*", )
]
)
@pytest.mark.usefixtures("limited_recursion_depth")
def test_no_recursion_index_on_recursion_error(): def test_no_recursion_index_on_recursion_error():
""" """
Ensure that we don't break in case we can't find the recursion index Ensure that we don't break in case we can't find the recursion index
during a recursion error (#2486). during a recursion error (#2486).
""" """
try:
class RecursionDepthError(object): class RecursionDepthError(object):
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self, "_" + attr) return getattr(self, "_" + attr)
with pytest.raises(RuntimeError) as excinfo:
RecursionDepthError().trigger RecursionDepthError().trigger
except: # noqa assert "maximum recursion" in str(excinfo.getrepr())
from _pytest._code.code import ExceptionInfo
exc_info = ExceptionInfo()
assert "maximum recursion" in str(exc_info.getrepr())
else:
assert 0

View File

@ -27,16 +27,7 @@ def test_source_str_function():
x = Source( x = Source(
""" """
3 3
""",
rstrip=False,
)
assert str(x) == "\n3\n "
x = Source(
""" """
3
""",
rstrip=True,
) )
assert str(x) == "\n3" assert str(x) == "\n3"
@ -400,10 +391,13 @@ def test_getfuncsource_with_multine_string():
pass pass
""" """
assert ( expected = '''\
str(_pytest._code.Source(f)).strip() def f():
== 'def f():\n c = """while True:\n pass\n"""' c = """while True:
) pass
"""
'''
assert str(_pytest._code.Source(f)) == expected.rstrip()
def test_deindent(): def test_deindent():
@ -411,21 +405,13 @@ def test_deindent():
assert deindent(["\tfoo", "\tbar"]) == ["foo", "bar"] assert deindent(["\tfoo", "\tbar"]) == ["foo", "bar"]
def f(): source = """\
c = """while True:
pass
"""
lines = deindent(inspect.getsource(f).splitlines())
assert lines == ["def f():", ' c = """while True:', " pass", '"""']
source = """
def f(): def f():
def g(): def g():
pass pass
""" """
lines = deindent(source.splitlines()) lines = deindent(source.splitlines())
assert lines == ["", "def f():", " def g():", " pass", " "] assert lines == ["def f():", " def g():", " pass"]
def test_source_of_class_at_eof_without_newline(tmpdir): def test_source_of_class_at_eof_without_newline(tmpdir):

View File

@ -1,4 +1,3 @@
import pytest import pytest

View File

@ -1,4 +1,3 @@
import pytest import pytest

View File

@ -0,0 +1,20 @@
import pytest
@pytest.fixture
def dynamic():
pass
@pytest.fixture
def a(request):
request.getfixturevalue("dynamic")
@pytest.fixture
def b(a):
pass
def test(b, request):
assert request.fixturenames == ["b", "request", "a", "dynamic"]

View File

@ -1,4 +1,3 @@
import pytest import pytest
import pprint import pprint

View File

@ -0,0 +1,13 @@
import pytest
import unittest
@pytest.fixture(params=[1, 2])
def two(request):
return request.param
@pytest.mark.usefixtures("two")
class TestSomethingElse(unittest.TestCase):
def test_two(self):
pass

View File

@ -45,7 +45,7 @@ def test_change_level_undo(testdir):
assert 0 assert 0
""" """
) )
result = testdir.runpytest_subprocess() result = testdir.runpytest()
result.stdout.fnmatch_lines(["*log from test1*", "*2 failed in *"]) result.stdout.fnmatch_lines(["*log from test1*", "*2 failed in *"])
assert "log from test2" not in result.stdout.str() assert "log from test2" not in result.stdout.str()

View File

@ -62,11 +62,11 @@ class TestApprox(object):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"value, repr_string", "value, repr_string",
[ [
(5., "approx(5.0 {pm} 5.0e-06)"), (5.0, "approx(5.0 {pm} 5.0e-06)"),
([5.], "approx([5.0 {pm} 5.0e-06])"), ([5.0], "approx([5.0 {pm} 5.0e-06])"),
([[5.]], "approx([[5.0 {pm} 5.0e-06]])"), ([[5.0]], "approx([[5.0 {pm} 5.0e-06]])"),
([[5., 6.]], "approx([[5.0 {pm} 5.0e-06, 6.0 {pm} 6.0e-06]])"), ([[5.0, 6.0]], "approx([[5.0 {pm} 5.0e-06, 6.0 {pm} 6.0e-06]])"),
([[5.], [6.]], "approx([[5.0 {pm} 5.0e-06], [6.0 {pm} 6.0e-06]])"), ([[5.0], [6.0]], "approx([[5.0 {pm} 5.0e-06], [6.0 {pm} 6.0e-06]])"),
], ],
) )
def test_repr_nd_array(self, plus_minus, value, repr_string): def test_repr_nd_array(self, plus_minus, value, repr_string):
@ -354,16 +354,16 @@ class TestApprox(object):
Test all permutations of where the approx and np.array() can show up Test all permutations of where the approx and np.array() can show up
""" """
np = pytest.importorskip("numpy") np = pytest.importorskip("numpy")
expected = 100. expected = 100.0
actual = 99. actual = 99.0
abs_diff = expected - actual abs_diff = expected - actual
rel_diff = (expected - actual) / expected rel_diff = (expected - actual) / expected
tests = [ tests = [
(eq, abs_diff, 0), (eq, abs_diff, 0),
(eq, 0, rel_diff), (eq, 0, rel_diff),
(ne, 0, rel_diff / 2.), # rel diff fail (ne, 0, rel_diff / 2.0), # rel diff fail
(ne, abs_diff / 2., 0), # abs diff fail (ne, abs_diff / 2.0, 0), # abs diff fail
] ]
for op, _abs, _rel in tests: for op, _abs, _rel in tests:

View File

@ -756,6 +756,12 @@ class TestRequestBasic(object):
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=1) reprec.assertoutcome(passed=1)
def test_request_fixturenames_dynamic_fixture(self, testdir):
"""Regression test for #3057"""
testdir.copy_example("fixtures/test_getfixturevalue_dynamic.py")
result = testdir.runpytest()
result.stdout.fnmatch_lines("*1 passed*")
def test_funcargnames_compatattr(self, testdir): def test_funcargnames_compatattr(self, testdir):
testdir.makepyfile( testdir.makepyfile(
""" """
@ -3602,7 +3608,8 @@ class TestParameterizedSubRequest(object):
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
""" """
E*Failed: The requested fixture has no parameter defined for the current test. E*Failed: The requested fixture has no parameter defined for test:
E* test_call_from_fixture.py::test_foo
E* E*
E*Requested fixture 'fix_with_param' defined in: E*Requested fixture 'fix_with_param' defined in:
E*test_call_from_fixture.py:4 E*test_call_from_fixture.py:4
@ -3628,7 +3635,8 @@ class TestParameterizedSubRequest(object):
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
""" """
E*Failed: The requested fixture has no parameter defined for the current test. E*Failed: The requested fixture has no parameter defined for test:
E* test_call_from_test.py::test_foo
E* E*
E*Requested fixture 'fix_with_param' defined in: E*Requested fixture 'fix_with_param' defined in:
E*test_call_from_test.py:4 E*test_call_from_test.py:4
@ -3658,7 +3666,8 @@ class TestParameterizedSubRequest(object):
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
""" """
E*Failed: The requested fixture has no parameter defined for the current test. E*Failed: The requested fixture has no parameter defined for test:
E* test_external_fixture.py::test_foo
E* E*
E*Requested fixture 'fix_with_param' defined in: E*Requested fixture 'fix_with_param' defined in:
E*conftest.py:4 E*conftest.py:4
@ -3701,7 +3710,8 @@ class TestParameterizedSubRequest(object):
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(
""" """
E*Failed: The requested fixture has no parameter defined for the current test. E*Failed: The requested fixture has no parameter defined for test:
E* test_foos.py::test_foo
E* E*
E*Requested fixture 'fix_with_param' defined in: E*Requested fixture 'fix_with_param' defined in:
E*fix.py:4 E*fix.py:4

View File

@ -177,7 +177,7 @@ def test_makepyfile_unicode(testdir):
unichr(65) unichr(65)
except NameError: except NameError:
unichr = chr unichr = chr
testdir.makepyfile(unichr(0xfffd)) testdir.makepyfile(unichr(0xFFFD))
def test_makepyfile_utf8(testdir): def test_makepyfile_utf8(testdir):

View File

@ -1010,3 +1010,15 @@ def test_testcase_handles_init_exceptions(testdir):
result = testdir.runpytest() result = testdir.runpytest()
assert "should raise this exception" in result.stdout.str() assert "should raise this exception" in result.stdout.str()
assert "ERROR at teardown of MyTestCase.test_hello" not in result.stdout.str() assert "ERROR at teardown of MyTestCase.test_hello" not in result.stdout.str()
def test_error_message_with_parametrized_fixtures(testdir):
testdir.copy_example("unittest/test_parametrized_fixture_error_message.py")
result = testdir.runpytest()
result.stdout.fnmatch_lines(
[
"*test_two does not support fixtures*",
"*TestSomethingElse::test_two",
"*Function type: TestCaseFunction",
]
)