Merge branch 'main' into warn-when-a-mark-is-applied-to-a-fixture
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
import operator
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
from decimal import Decimal
|
||||
from fractions import Fraction
|
||||
from math import sqrt
|
||||
from operator import eq
|
||||
from operator import ne
|
||||
from typing import Optional
|
||||
|
||||
import pytest
|
||||
from _pytest.pytester import Pytester
|
||||
from _pytest.python_api import _recursive_sequence_map
|
||||
from pytest import approx
|
||||
|
||||
inf, nan = float("inf"), float("nan")
|
||||
@@ -43,7 +45,250 @@ def mocked_doctest_runner(monkeypatch):
|
||||
return MyDocTestRunner()
|
||||
|
||||
|
||||
@contextmanager
|
||||
def temporary_verbosity(config, verbosity=0):
|
||||
original_verbosity = config.getoption("verbose")
|
||||
config.option.verbose = verbosity
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
config.option.verbose = original_verbosity
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def assert_approx_raises_regex(pytestconfig):
|
||||
def do_assert(lhs, rhs, expected_message, verbosity_level=0):
|
||||
import re
|
||||
|
||||
with temporary_verbosity(pytestconfig, verbosity_level):
|
||||
with pytest.raises(AssertionError) as e:
|
||||
assert lhs == approx(rhs)
|
||||
|
||||
nl = "\n"
|
||||
obtained_message = str(e.value).splitlines()[1:]
|
||||
assert len(obtained_message) == len(expected_message), (
|
||||
"Regex message length doesn't match obtained.\n"
|
||||
"Obtained:\n"
|
||||
f"{nl.join(obtained_message)}\n\n"
|
||||
"Expected regex:\n"
|
||||
f"{nl.join(expected_message)}\n\n"
|
||||
)
|
||||
|
||||
for i, (obtained_line, expected_line) in enumerate(
|
||||
zip(obtained_message, expected_message)
|
||||
):
|
||||
regex = re.compile(expected_line)
|
||||
assert regex.match(obtained_line) is not None, (
|
||||
"Unexpected error message:\n"
|
||||
f"{nl.join(obtained_message)}\n\n"
|
||||
"Did not match regex:\n"
|
||||
f"{nl.join(expected_message)}\n\n"
|
||||
f"With verbosity level = {verbosity_level}, on line {i}"
|
||||
)
|
||||
|
||||
return do_assert
|
||||
|
||||
|
||||
SOME_FLOAT = r"[+-]?([0-9]*[.])?[0-9]+\s*"
|
||||
SOME_INT = r"[0-9]+\s*"
|
||||
|
||||
|
||||
class TestApprox:
|
||||
def test_error_messages_native_dtypes(self, assert_approx_raises_regex):
|
||||
assert_approx_raises_regex(
|
||||
2.0,
|
||||
1.0,
|
||||
[
|
||||
" comparison failed",
|
||||
f" Obtained: {SOME_FLOAT}",
|
||||
f" Expected: {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||
],
|
||||
)
|
||||
|
||||
assert_approx_raises_regex(
|
||||
{"a": 1.0, "b": 1000.0, "c": 1000000.0},
|
||||
{
|
||||
"a": 2.0,
|
||||
"b": 1000.0,
|
||||
"c": 3000000.0,
|
||||
},
|
||||
[
|
||||
r" comparison failed. Mismatched elements: 2 / 3:",
|
||||
rf" Max absolute difference: {SOME_FLOAT}",
|
||||
rf" Max relative difference: {SOME_FLOAT}",
|
||||
r" Index \| Obtained\s+\| Expected ",
|
||||
rf" a \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||
rf" c \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||
],
|
||||
)
|
||||
|
||||
assert_approx_raises_regex(
|
||||
[1.0, 2.0, 3.0, 4.0],
|
||||
[1.0, 3.0, 3.0, 5.0],
|
||||
[
|
||||
r" comparison failed. Mismatched elements: 2 / 4:",
|
||||
rf" Max absolute difference: {SOME_FLOAT}",
|
||||
rf" Max relative difference: {SOME_FLOAT}",
|
||||
r" Index \| Obtained\s+\| Expected ",
|
||||
rf" 1 \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||
rf" 3 \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||
],
|
||||
)
|
||||
|
||||
assert_approx_raises_regex(
|
||||
(1, 2.2, 4),
|
||||
(1, 3.2, 4),
|
||||
[
|
||||
r" comparison failed. Mismatched elements: 1 / 3:",
|
||||
rf" Max absolute difference: {SOME_FLOAT}",
|
||||
rf" Max relative difference: {SOME_FLOAT}",
|
||||
r" Index \| Obtained\s+\| Expected ",
|
||||
rf" 1 \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||
],
|
||||
)
|
||||
|
||||
# Specific test for comparison with 0.0 (relative diff will be 'inf')
|
||||
assert_approx_raises_regex(
|
||||
[0.0],
|
||||
[1.0],
|
||||
[
|
||||
r" comparison failed. Mismatched elements: 1 / 1:",
|
||||
rf" Max absolute difference: {SOME_FLOAT}",
|
||||
r" Max relative difference: inf",
|
||||
r" Index \| Obtained\s+\| Expected ",
|
||||
rf"\s*0\s*\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||
],
|
||||
)
|
||||
|
||||
def test_error_messages_numpy_dtypes(self, assert_approx_raises_regex):
|
||||
np = pytest.importorskip("numpy")
|
||||
|
||||
a = np.linspace(0, 100, 20)
|
||||
b = np.linspace(0, 100, 20)
|
||||
a[10] += 0.5
|
||||
assert_approx_raises_regex(
|
||||
a,
|
||||
b,
|
||||
[
|
||||
r" comparison failed. Mismatched elements: 1 / 20:",
|
||||
rf" Max absolute difference: {SOME_FLOAT}",
|
||||
rf" Max relative difference: {SOME_FLOAT}",
|
||||
r" Index \| Obtained\s+\| Expected",
|
||||
rf" \(10,\) \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||
],
|
||||
)
|
||||
|
||||
assert_approx_raises_regex(
|
||||
np.array(
|
||||
[
|
||||
[[1.1987311, 12412342.3], [3.214143244, 1423412423415.677]],
|
||||
[[1, 2], [3, 219371297321973]],
|
||||
]
|
||||
),
|
||||
np.array(
|
||||
[
|
||||
[[1.12313, 12412342.3], [3.214143244, 534523542345.677]],
|
||||
[[1, 2], [3, 7]],
|
||||
]
|
||||
),
|
||||
[
|
||||
r" comparison failed. Mismatched elements: 3 / 8:",
|
||||
rf" Max absolute difference: {SOME_FLOAT}",
|
||||
rf" Max relative difference: {SOME_FLOAT}",
|
||||
r" Index\s+\| Obtained\s+\| Expected\s+",
|
||||
rf" \(0, 0, 0\) \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||
rf" \(0, 1, 1\) \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||
rf" \(1, 1, 1\) \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||
],
|
||||
)
|
||||
|
||||
# Specific test for comparison with 0.0 (relative diff will be 'inf')
|
||||
assert_approx_raises_regex(
|
||||
np.array([0.0]),
|
||||
np.array([1.0]),
|
||||
[
|
||||
r" comparison failed. Mismatched elements: 1 / 1:",
|
||||
rf" Max absolute difference: {SOME_FLOAT}",
|
||||
r" Max relative difference: inf",
|
||||
r" Index \| Obtained\s+\| Expected ",
|
||||
rf"\s*\(0,\)\s*\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||
],
|
||||
)
|
||||
|
||||
def test_error_messages_invalid_args(self, assert_approx_raises_regex):
|
||||
np = pytest.importorskip("numpy")
|
||||
with pytest.raises(AssertionError) as e:
|
||||
assert np.array([[1.2, 3.4], [4.0, 5.0]]) == pytest.approx(
|
||||
np.array([[4.0], [5.0]])
|
||||
)
|
||||
message = "\n".join(str(e.value).split("\n")[1:])
|
||||
assert message == "\n".join(
|
||||
[
|
||||
" Impossible to compare arrays with different shapes.",
|
||||
" Shapes: (2, 1) and (2, 2)",
|
||||
]
|
||||
)
|
||||
|
||||
with pytest.raises(AssertionError) as e:
|
||||
assert [1.0, 2.0, 3.0] == pytest.approx([4.0, 5.0])
|
||||
message = "\n".join(str(e.value).split("\n")[1:])
|
||||
assert message == "\n".join(
|
||||
[
|
||||
" Impossible to compare lists with different sizes.",
|
||||
" Lengths: 2 and 3",
|
||||
]
|
||||
)
|
||||
|
||||
def test_error_messages_with_different_verbosity(self, assert_approx_raises_regex):
|
||||
np = pytest.importorskip("numpy")
|
||||
for v in [0, 1, 2]:
|
||||
# Verbosity level doesn't affect the error message for scalars
|
||||
assert_approx_raises_regex(
|
||||
2.0,
|
||||
1.0,
|
||||
[
|
||||
" comparison failed",
|
||||
f" Obtained: {SOME_FLOAT}",
|
||||
f" Expected: {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||
],
|
||||
verbosity_level=v,
|
||||
)
|
||||
|
||||
a = np.linspace(1, 101, 20)
|
||||
b = np.linspace(2, 102, 20)
|
||||
assert_approx_raises_regex(
|
||||
a,
|
||||
b,
|
||||
[
|
||||
r" comparison failed. Mismatched elements: 20 / 20:",
|
||||
rf" Max absolute difference: {SOME_FLOAT}",
|
||||
rf" Max relative difference: {SOME_FLOAT}",
|
||||
r" Index \| Obtained\s+\| Expected",
|
||||
rf" \(0,\)\s+\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||
rf" \(1,\)\s+\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}",
|
||||
rf" \(2,\)\s+\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}...",
|
||||
"",
|
||||
rf"\s*...Full output truncated \({SOME_INT} lines hidden\), use '-vv' to show",
|
||||
],
|
||||
verbosity_level=0,
|
||||
)
|
||||
|
||||
assert_approx_raises_regex(
|
||||
a,
|
||||
b,
|
||||
[
|
||||
r" comparison failed. Mismatched elements: 20 / 20:",
|
||||
rf" Max absolute difference: {SOME_FLOAT}",
|
||||
rf" Max relative difference: {SOME_FLOAT}",
|
||||
r" Index \| Obtained\s+\| Expected",
|
||||
]
|
||||
+ [
|
||||
rf" \({i},\)\s+\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}"
|
||||
for i in range(20)
|
||||
],
|
||||
verbosity_level=2,
|
||||
)
|
||||
|
||||
def test_repr_string(self):
|
||||
assert repr(approx(1.0)) == "1.0 ± 1.0e-06"
|
||||
assert repr(approx([1.0, 2.0])) == "approx([1.0 ± 1.0e-06, 2.0 ± 2.0e-06])"
|
||||
@@ -89,6 +334,12 @@ class TestApprox:
|
||||
np_array = np.array(value)
|
||||
assert repr(approx(np_array)) == expected_repr_string
|
||||
|
||||
def test_bool(self):
|
||||
with pytest.raises(AssertionError) as err:
|
||||
assert approx(1)
|
||||
|
||||
assert err.match(r"approx\(\) is not supported in a boolean context")
|
||||
|
||||
def test_operator_overloading(self):
|
||||
assert 1 == approx(1, rel=1e-6, abs=1e-12)
|
||||
assert not (1 != approx(1, rel=1e-6, abs=1e-12))
|
||||
@@ -536,7 +787,7 @@ class TestApprox:
|
||||
def test_expected_value_type_error(self, x, name):
|
||||
with pytest.raises(
|
||||
TypeError,
|
||||
match=fr"pytest.approx\(\) does not support nested {name}:",
|
||||
match=rf"pytest.approx\(\) does not support nested {name}:",
|
||||
):
|
||||
approx(x)
|
||||
|
||||
@@ -574,7 +825,6 @@ class TestApprox:
|
||||
assert 1.0 != approx([None])
|
||||
assert None != approx([1.0]) # noqa: E711
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires ordered dicts")
|
||||
def test_nonnumeric_dict_repr(self):
|
||||
"""Dicts with non-numerics and infinites have no tolerances"""
|
||||
x1 = {"foo": 1.0000005, "bar": None, "foobar": inf}
|
||||
@@ -624,13 +874,49 @@ class TestApprox:
|
||||
assert approx(expected, rel=5e-7, abs=0) == actual
|
||||
assert approx(expected, rel=5e-8, abs=0) != actual
|
||||
|
||||
def test_generic_sized_iterable_object(self):
|
||||
class MySizedIterable:
|
||||
def __iter__(self):
|
||||
return iter([1, 2, 3, 4])
|
||||
def test_generic_ordered_sequence(self):
|
||||
class MySequence:
|
||||
def __getitem__(self, i):
|
||||
return [1, 2, 3, 4][i]
|
||||
|
||||
def __len__(self):
|
||||
return 4
|
||||
|
||||
expected = MySizedIterable()
|
||||
assert [1, 2, 3, 4] == approx(expected)
|
||||
expected = MySequence()
|
||||
assert [1, 2, 3, 4] == approx(expected, abs=1e-4)
|
||||
|
||||
expected_repr = "approx([1 ± 1.0e-06, 2 ± 2.0e-06, 3 ± 3.0e-06, 4 ± 4.0e-06])"
|
||||
assert repr(approx(expected)) == expected_repr
|
||||
|
||||
def test_allow_ordered_sequences_only(self) -> None:
|
||||
"""pytest.approx() should raise an error on unordered sequences (#9692)."""
|
||||
with pytest.raises(TypeError, match="only supports ordered sequences"):
|
||||
assert {1, 2, 3} == approx({1, 2, 3})
|
||||
|
||||
|
||||
class TestRecursiveSequenceMap:
|
||||
def test_map_over_scalar(self):
|
||||
assert _recursive_sequence_map(sqrt, 16) == 4
|
||||
|
||||
def test_map_over_empty_list(self):
|
||||
assert _recursive_sequence_map(sqrt, []) == []
|
||||
|
||||
def test_map_over_list(self):
|
||||
assert _recursive_sequence_map(sqrt, [4, 16, 25, 676]) == [2, 4, 5, 26]
|
||||
|
||||
def test_map_over_tuple(self):
|
||||
assert _recursive_sequence_map(sqrt, (4, 16, 25, 676)) == (2, 4, 5, 26)
|
||||
|
||||
def test_map_over_nested_lists(self):
|
||||
assert _recursive_sequence_map(sqrt, [4, [25, 64], [[49]]]) == [
|
||||
2,
|
||||
[5, 8],
|
||||
[[7]],
|
||||
]
|
||||
|
||||
def test_map_over_mixed_sequence(self):
|
||||
assert _recursive_sequence_map(sqrt, [4, (25, 64), [(49)]]) == [
|
||||
2,
|
||||
(5, 8),
|
||||
[(7)],
|
||||
]
|
||||
|
||||
@@ -7,11 +7,12 @@ from typing import Dict
|
||||
import _pytest._code
|
||||
import pytest
|
||||
from _pytest.config import ExitCode
|
||||
from _pytest.main import Session
|
||||
from _pytest.monkeypatch import MonkeyPatch
|
||||
from _pytest.nodes import Collector
|
||||
from _pytest.pytester import Pytester
|
||||
from _pytest.python import Class
|
||||
from _pytest.python import Instance
|
||||
from _pytest.python import Function
|
||||
|
||||
|
||||
class TestModule:
|
||||
@@ -294,7 +295,7 @@ class TestFunction:
|
||||
from _pytest.fixtures import FixtureManager
|
||||
|
||||
config = pytester.parseconfigure()
|
||||
session = pytester.Session.from_config(config)
|
||||
session = Session.from_config(config)
|
||||
session._fixturemanager = FixtureManager(session)
|
||||
|
||||
return pytest.Function.from_parent(parent=session, **kwargs)
|
||||
@@ -584,7 +585,7 @@ class TestFunction:
|
||||
pass
|
||||
"""
|
||||
)
|
||||
colitems = modcol.collect()[0].collect()[0].collect()
|
||||
colitems = modcol.collect()[0].collect()
|
||||
assert colitems[0].name == "test1[a-c]"
|
||||
assert colitems[1].name == "test1[b-c]"
|
||||
assert colitems[2].name == "test2[a-c]"
|
||||
@@ -770,6 +771,36 @@ class TestSorting:
|
||||
assert len(colitems) == 2
|
||||
assert [item.name for item in colitems] == ["test_b", "test_a"]
|
||||
|
||||
def test_ordered_by_definition_order(self, pytester: Pytester) -> None:
|
||||
pytester.makepyfile(
|
||||
"""\
|
||||
class Test1:
|
||||
def test_foo(): pass
|
||||
def test_bar(): pass
|
||||
class Test2:
|
||||
def test_foo(): pass
|
||||
test_bar = Test1.test_bar
|
||||
class Test3(Test2):
|
||||
def test_baz(): pass
|
||||
"""
|
||||
)
|
||||
result = pytester.runpytest("--collect-only")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*Class Test1*",
|
||||
"*Function test_foo*",
|
||||
"*Function test_bar*",
|
||||
"*Class Test2*",
|
||||
# previously the order was flipped due to Test1.test_bar reference
|
||||
"*Function test_foo*",
|
||||
"*Function test_bar*",
|
||||
"*Class Test3*",
|
||||
"*Function test_foo*",
|
||||
"*Function test_bar*",
|
||||
"*Function test_baz*",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class TestConftestCustomization:
|
||||
def test_pytest_pycollect_module(self, pytester: Pytester) -> None:
|
||||
@@ -778,9 +809,9 @@ class TestConftestCustomization:
|
||||
import pytest
|
||||
class MyModule(pytest.Module):
|
||||
pass
|
||||
def pytest_pycollect_makemodule(fspath, parent):
|
||||
if fspath.name == "test_xyz.py":
|
||||
return MyModule.from_parent(path=fspath, parent=parent)
|
||||
def pytest_pycollect_makemodule(module_path, parent):
|
||||
if module_path.name == "test_xyz.py":
|
||||
return MyModule.from_parent(path=module_path, parent=parent)
|
||||
"""
|
||||
)
|
||||
pytester.makepyfile("def test_some(): pass")
|
||||
@@ -882,9 +913,9 @@ class TestConftestCustomization:
|
||||
return Loader()
|
||||
sys.meta_path.append(Finder())
|
||||
|
||||
def pytest_collect_file(fspath, parent):
|
||||
if fspath.suffix == ".narf":
|
||||
return Module.from_parent(path=fspath, parent=parent)"""
|
||||
def pytest_collect_file(file_path, parent):
|
||||
if file_path.suffix == ".narf":
|
||||
return Module.from_parent(path=file_path, parent=parent)"""
|
||||
)
|
||||
pytester.makefile(
|
||||
".narf",
|
||||
@@ -1124,8 +1155,8 @@ class TestReportInfo:
|
||||
|
||||
def test_func_reportinfo(self, pytester: Pytester) -> None:
|
||||
item = pytester.getitem("def test_func(): pass")
|
||||
fspath, lineno, modpath = item.reportinfo()
|
||||
assert str(fspath) == str(item.path)
|
||||
path, lineno, modpath = item.reportinfo()
|
||||
assert os.fspath(path) == str(item.path)
|
||||
assert lineno == 0
|
||||
assert modpath == "test_func"
|
||||
|
||||
@@ -1139,8 +1170,8 @@ class TestReportInfo:
|
||||
)
|
||||
classcol = pytester.collect_by_name(modcol, "TestClass")
|
||||
assert isinstance(classcol, Class)
|
||||
fspath, lineno, msg = classcol.reportinfo()
|
||||
assert str(fspath) == str(modcol.path)
|
||||
path, lineno, msg = classcol.reportinfo()
|
||||
assert os.fspath(path) == str(modcol.path)
|
||||
assert lineno == 1
|
||||
assert msg == "TestClass"
|
||||
|
||||
@@ -1152,19 +1183,26 @@ class TestReportInfo:
|
||||
modcol = pytester.getmodulecol(
|
||||
"""
|
||||
# lineno 0
|
||||
class TestClass(object):
|
||||
class TestClass:
|
||||
def __getattr__(self, name):
|
||||
return "this is not an int"
|
||||
|
||||
def __class_getattr__(cls, name):
|
||||
return "this is not an int"
|
||||
|
||||
def intest_foo(self):
|
||||
pass
|
||||
|
||||
def test_bar(self):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
classcol = pytester.collect_by_name(modcol, "TestClass")
|
||||
assert isinstance(classcol, Class)
|
||||
instance = list(classcol.collect())[0]
|
||||
assert isinstance(instance, Instance)
|
||||
fspath, lineno, msg = instance.reportinfo()
|
||||
path, lineno, msg = classcol.reportinfo()
|
||||
func = list(classcol.collect())[0]
|
||||
assert isinstance(func, Function)
|
||||
path, lineno, msg = func.reportinfo()
|
||||
|
||||
|
||||
def test_customized_python_discovery(pytester: Pytester) -> None:
|
||||
@@ -1237,7 +1275,7 @@ def test_unorderable_types(pytester: Pytester) -> None:
|
||||
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("default")
|
||||
@pytest.mark.filterwarnings("default::pytest.PytestCollectionWarning")
|
||||
def test_dont_collect_non_function_callable(pytester: Pytester) -> None:
|
||||
"""Test for issue https://github.com/pytest-dev/pytest/issues/331
|
||||
|
||||
|
||||
@@ -103,10 +103,6 @@ def test_getfuncargnames_staticmethod_partial():
|
||||
|
||||
@pytest.mark.pytester_example_path("fixtures/fill_fixtures")
|
||||
class TestFillFixtures:
|
||||
def test_fillfuncargs_exposed(self):
|
||||
# used by oejskit, kept for compatibility
|
||||
assert pytest._fillfuncargs == fixtures._fillfuncargs
|
||||
|
||||
def test_funcarg_lookupfails(self, pytester: Pytester) -> None:
|
||||
pytester.copy_example()
|
||||
result = pytester.runpytest() # "--collect-only")
|
||||
@@ -967,8 +963,6 @@ class TestRequestBasic:
|
||||
(item,) = pytester.genitems([modcol])
|
||||
req = fixtures.FixtureRequest(item, _ispytest=True)
|
||||
assert req.path == modcol.path
|
||||
with pytest.warns(pytest.PytestDeprecationWarning):
|
||||
assert req.fspath == modcol.fspath
|
||||
|
||||
def test_request_fixturenames(self, pytester: Pytester) -> None:
|
||||
pytester.makepyfile(
|
||||
@@ -1094,6 +1088,20 @@ class TestRequestBasic:
|
||||
reprec.assertoutcome(passed=2)
|
||||
|
||||
|
||||
class TestRequestSessionScoped:
|
||||
@pytest.fixture(scope="session")
|
||||
def session_request(self, request):
|
||||
return request
|
||||
|
||||
@pytest.mark.parametrize("name", ["path", "module"])
|
||||
def test_session_scoped_unavailable_attributes(self, session_request, name):
|
||||
with pytest.raises(
|
||||
AttributeError,
|
||||
match=f"{name} not available in session-scoped context",
|
||||
):
|
||||
getattr(session_request, name)
|
||||
|
||||
|
||||
class TestRequestMarking:
|
||||
def test_applymarker(self, pytester: Pytester) -> None:
|
||||
item1, item2 = pytester.getitems(
|
||||
@@ -2069,9 +2077,9 @@ class TestAutouseManagement:
|
||||
reprec = pytester.inline_run("-v", "-s", "--confcutdir", pytester.path)
|
||||
reprec.assertoutcome(passed=8)
|
||||
config = reprec.getcalls("pytest_unconfigure")[0].config
|
||||
values = config.pluginmanager._getconftestmodules(p, importmode="prepend")[
|
||||
0
|
||||
].values
|
||||
values = config.pluginmanager._getconftestmodules(
|
||||
p, importmode="prepend", rootpath=pytester.path
|
||||
)[0].values
|
||||
assert values == ["fin_a1", "fin_a2", "fin_b1", "fin_b2"] * 2
|
||||
|
||||
def test_scope_ordering(self, pytester: Pytester) -> None:
|
||||
@@ -3334,9 +3342,9 @@ class TestShowFixtures:
|
||||
result = pytester.runpytest("--fixtures")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"tmp_path_factory [[]session scope[]]",
|
||||
"tmp_path_factory [[]session scope[]] -- .../_pytest/tmpdir.py:*",
|
||||
"*for the test session*",
|
||||
"tmp_path",
|
||||
"tmp_path -- .../_pytest/tmpdir.py:*",
|
||||
"*temporary directory*",
|
||||
]
|
||||
)
|
||||
@@ -3345,9 +3353,9 @@ class TestShowFixtures:
|
||||
result = pytester.runpytest("--fixtures", "-v")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"tmp_path_factory [[]session scope[]] -- *tmpdir.py*",
|
||||
"tmp_path_factory [[]session scope[]] -- .../_pytest/tmpdir.py:*",
|
||||
"*for the test session*",
|
||||
"tmp_path -- *tmpdir.py*",
|
||||
"tmp_path -- .../_pytest/tmpdir.py:*",
|
||||
"*temporary directory*",
|
||||
]
|
||||
)
|
||||
@@ -3367,9 +3375,9 @@ class TestShowFixtures:
|
||||
result = pytester.runpytest("--fixtures", p)
|
||||
result.stdout.fnmatch_lines(
|
||||
"""
|
||||
*tmp_path
|
||||
*tmp_path -- *
|
||||
*fixtures defined from*
|
||||
*arg1*
|
||||
*arg1 -- test_show_fixtures_testmodule.py:6*
|
||||
*hello world*
|
||||
"""
|
||||
)
|
||||
@@ -3429,10 +3437,10 @@ class TestShowFixtures:
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
* fixtures defined from test_show_fixtures_trimmed_doc *
|
||||
arg2
|
||||
arg2 -- test_show_fixtures_trimmed_doc.py:10
|
||||
line1
|
||||
line2
|
||||
arg1
|
||||
arg1 -- test_show_fixtures_trimmed_doc.py:3
|
||||
line1
|
||||
line2
|
||||
"""
|
||||
@@ -3458,7 +3466,7 @@ class TestShowFixtures:
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
* fixtures defined from test_show_fixtures_indented_doc *
|
||||
fixture1
|
||||
fixture1 -- test_show_fixtures_indented_doc.py:3
|
||||
line1
|
||||
indented line
|
||||
"""
|
||||
@@ -3486,7 +3494,7 @@ class TestShowFixtures:
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
* fixtures defined from test_show_fixtures_indented_doc_first_line_unindented *
|
||||
fixture1
|
||||
fixture1 -- test_show_fixtures_indented_doc_first_line_unindented.py:3
|
||||
line1
|
||||
line2
|
||||
indented line
|
||||
@@ -3514,7 +3522,7 @@ class TestShowFixtures:
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
* fixtures defined from test_show_fixtures_indented_in_class *
|
||||
fixture1
|
||||
fixture1 -- test_show_fixtures_indented_in_class.py:4
|
||||
line1
|
||||
line2
|
||||
indented line
|
||||
@@ -3554,11 +3562,11 @@ class TestShowFixtures:
|
||||
result.stdout.fnmatch_lines(
|
||||
"""
|
||||
* fixtures defined from test_a *
|
||||
fix_a
|
||||
fix_a -- test_a.py:4
|
||||
Fixture A
|
||||
|
||||
* fixtures defined from test_b *
|
||||
fix_b
|
||||
fix_b -- test_b.py:4
|
||||
Fixture B
|
||||
"""
|
||||
)
|
||||
@@ -3594,11 +3602,11 @@ class TestShowFixtures:
|
||||
result.stdout.fnmatch_lines(
|
||||
"""
|
||||
* fixtures defined from conftest *
|
||||
arg1
|
||||
arg1 -- conftest.py:3
|
||||
Hello World in conftest.py
|
||||
|
||||
* fixtures defined from test_show_fixtures_with_same_name *
|
||||
arg1
|
||||
arg1 -- test_show_fixtures_with_same_name.py:3
|
||||
Hi from test module
|
||||
"""
|
||||
)
|
||||
|
||||
@@ -1,81 +1,8 @@
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from _pytest import runner
|
||||
from _pytest._code import getfslineno
|
||||
from _pytest.fixtures import getfixturemarker
|
||||
from _pytest.pytester import Pytester
|
||||
|
||||
|
||||
class TestOEJSKITSpecials:
|
||||
def test_funcarg_non_pycollectobj(
|
||||
self, pytester: Pytester, recwarn
|
||||
) -> None: # rough jstests usage
|
||||
pytester.makeconftest(
|
||||
"""
|
||||
import pytest
|
||||
def pytest_pycollect_makeitem(collector, name, obj):
|
||||
if name == "MyClass":
|
||||
return MyCollector.from_parent(collector, name=name)
|
||||
class MyCollector(pytest.Collector):
|
||||
def reportinfo(self):
|
||||
return self.fspath, 3, "xyz"
|
||||
"""
|
||||
)
|
||||
modcol = pytester.getmodulecol(
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def arg1(request):
|
||||
return 42
|
||||
class MyClass(object):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
# this hook finds funcarg factories
|
||||
rep = runner.collect_one_node(collector=modcol)
|
||||
# TODO: Don't treat as Any.
|
||||
clscol: Any = rep.result[0]
|
||||
clscol.obj = lambda arg1: None
|
||||
clscol.funcargs = {}
|
||||
pytest._fillfuncargs(clscol)
|
||||
assert clscol.funcargs["arg1"] == 42
|
||||
|
||||
def test_autouse_fixture(
|
||||
self, pytester: Pytester, recwarn
|
||||
) -> None: # rough jstests usage
|
||||
pytester.makeconftest(
|
||||
"""
|
||||
import pytest
|
||||
def pytest_pycollect_makeitem(collector, name, obj):
|
||||
if name == "MyClass":
|
||||
return MyCollector.from_parent(collector, name=name)
|
||||
class MyCollector(pytest.Collector):
|
||||
def reportinfo(self):
|
||||
return self.fspath, 3, "xyz"
|
||||
"""
|
||||
)
|
||||
modcol = pytester.getmodulecol(
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture(autouse=True)
|
||||
def hello():
|
||||
pass
|
||||
@pytest.fixture
|
||||
def arg1(request):
|
||||
return 42
|
||||
class MyClass(object):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
# this hook finds funcarg factories
|
||||
rep = runner.collect_one_node(modcol)
|
||||
# TODO: Don't treat as Any.
|
||||
clscol: Any = rep.result[0]
|
||||
clscol.obj = lambda: None
|
||||
clscol.funcargs = {}
|
||||
pytest._fillfuncargs(clscol)
|
||||
assert not clscol.funcargs
|
||||
from _pytest.python import Function
|
||||
|
||||
|
||||
def test_wrapped_getfslineno() -> None:
|
||||
@@ -475,3 +402,28 @@ class TestParameterize:
|
||||
)
|
||||
res = pytester.runpytest("--collect-only")
|
||||
res.stdout.fnmatch_lines(["*spam-2*", "*ham-2*"])
|
||||
|
||||
|
||||
def test_function_instance(pytester: Pytester) -> None:
|
||||
items = pytester.getitems(
|
||||
"""
|
||||
def test_func(): pass
|
||||
class TestIt:
|
||||
def test_method(self): pass
|
||||
@classmethod
|
||||
def test_class(cls): pass
|
||||
@staticmethod
|
||||
def test_static(): pass
|
||||
"""
|
||||
)
|
||||
assert len(items) == 3
|
||||
assert isinstance(items[0], Function)
|
||||
assert items[0].name == "test_func"
|
||||
assert items[0].instance is None
|
||||
assert isinstance(items[1], Function)
|
||||
assert items[1].name == "test_method"
|
||||
assert items[1].instance is not None
|
||||
assert items[1].instance.__class__.__name__ == "TestIt"
|
||||
assert isinstance(items[2], Function)
|
||||
assert items[2].name == "test_static"
|
||||
assert items[2].instance is None
|
||||
|
||||
@@ -24,8 +24,8 @@ from _pytest.compat import getfuncargnames
|
||||
from _pytest.compat import NOTSET
|
||||
from _pytest.outcomes import fail
|
||||
from _pytest.pytester import Pytester
|
||||
from _pytest.python import _idval
|
||||
from _pytest.python import idmaker
|
||||
from _pytest.python import IdMaker
|
||||
from _pytest.scope import Scope
|
||||
|
||||
|
||||
class TestMetafunc:
|
||||
@@ -106,8 +106,8 @@ class TestMetafunc:
|
||||
with pytest.raises(
|
||||
fail.Exception,
|
||||
match=(
|
||||
r"In func: ids must be list of string/float/int/bool, found:"
|
||||
r" Exc\(from_gen\) \(type: <class .*Exc'>\) at index 2"
|
||||
r"In func: ids contains unsupported value Exc\(from_gen\) \(type: <class .*Exc'>\) at index 2. "
|
||||
r"Supported types are: .*"
|
||||
),
|
||||
):
|
||||
metafunc.parametrize("x", [1, 2, 3], ids=gen()) # type: ignore[arg-type]
|
||||
@@ -142,16 +142,16 @@ class TestMetafunc:
|
||||
|
||||
@attr.s
|
||||
class DummyFixtureDef:
|
||||
scope = attr.ib()
|
||||
_scope = attr.ib()
|
||||
|
||||
fixtures_defs = cast(
|
||||
Dict[str, Sequence[fixtures.FixtureDef[object]]],
|
||||
dict(
|
||||
session_fix=[DummyFixtureDef("session")],
|
||||
package_fix=[DummyFixtureDef("package")],
|
||||
module_fix=[DummyFixtureDef("module")],
|
||||
class_fix=[DummyFixtureDef("class")],
|
||||
func_fix=[DummyFixtureDef("function")],
|
||||
session_fix=[DummyFixtureDef(Scope.Session)],
|
||||
package_fix=[DummyFixtureDef(Scope.Package)],
|
||||
module_fix=[DummyFixtureDef(Scope.Module)],
|
||||
class_fix=[DummyFixtureDef(Scope.Class)],
|
||||
func_fix=[DummyFixtureDef(Scope.Function)],
|
||||
),
|
||||
)
|
||||
|
||||
@@ -160,29 +160,33 @@ class TestMetafunc:
|
||||
def find_scope(argnames, indirect):
|
||||
return _find_parametrized_scope(argnames, fixtures_defs, indirect=indirect)
|
||||
|
||||
assert find_scope(["func_fix"], indirect=True) == "function"
|
||||
assert find_scope(["class_fix"], indirect=True) == "class"
|
||||
assert find_scope(["module_fix"], indirect=True) == "module"
|
||||
assert find_scope(["package_fix"], indirect=True) == "package"
|
||||
assert find_scope(["session_fix"], indirect=True) == "session"
|
||||
assert find_scope(["func_fix"], indirect=True) == Scope.Function
|
||||
assert find_scope(["class_fix"], indirect=True) == Scope.Class
|
||||
assert find_scope(["module_fix"], indirect=True) == Scope.Module
|
||||
assert find_scope(["package_fix"], indirect=True) == Scope.Package
|
||||
assert find_scope(["session_fix"], indirect=True) == Scope.Session
|
||||
|
||||
assert find_scope(["class_fix", "func_fix"], indirect=True) == "function"
|
||||
assert find_scope(["func_fix", "session_fix"], indirect=True) == "function"
|
||||
assert find_scope(["session_fix", "class_fix"], indirect=True) == "class"
|
||||
assert find_scope(["package_fix", "session_fix"], indirect=True) == "package"
|
||||
assert find_scope(["module_fix", "session_fix"], indirect=True) == "module"
|
||||
assert find_scope(["class_fix", "func_fix"], indirect=True) == Scope.Function
|
||||
assert find_scope(["func_fix", "session_fix"], indirect=True) == Scope.Function
|
||||
assert find_scope(["session_fix", "class_fix"], indirect=True) == Scope.Class
|
||||
assert (
|
||||
find_scope(["package_fix", "session_fix"], indirect=True) == Scope.Package
|
||||
)
|
||||
assert find_scope(["module_fix", "session_fix"], indirect=True) == Scope.Module
|
||||
|
||||
# when indirect is False or is not for all scopes, always use function
|
||||
assert find_scope(["session_fix", "module_fix"], indirect=False) == "function"
|
||||
assert (
|
||||
find_scope(["session_fix", "module_fix"], indirect=False) == Scope.Function
|
||||
)
|
||||
assert (
|
||||
find_scope(["session_fix", "module_fix"], indirect=["module_fix"])
|
||||
== "function"
|
||||
== Scope.Function
|
||||
)
|
||||
assert (
|
||||
find_scope(
|
||||
["session_fix", "module_fix"], indirect=["session_fix", "module_fix"]
|
||||
)
|
||||
== "module"
|
||||
== Scope.Module
|
||||
)
|
||||
|
||||
def test_parametrize_and_id(self) -> None:
|
||||
@@ -281,7 +285,7 @@ class TestMetafunc:
|
||||
deadline=400.0
|
||||
) # very close to std deadline and CI boxes are not reliable in CPU power
|
||||
def test_idval_hypothesis(self, value) -> None:
|
||||
escaped = _idval(value, "a", 6, None, nodeid=None, config=None)
|
||||
escaped = IdMaker([], [], None, None, None, None, None)._idval(value, "a", 6)
|
||||
assert isinstance(escaped, str)
|
||||
escaped.encode("ascii")
|
||||
|
||||
@@ -303,7 +307,10 @@ class TestMetafunc:
|
||||
),
|
||||
]
|
||||
for val, expected in values:
|
||||
assert _idval(val, "a", 6, None, nodeid=None, config=None) == expected
|
||||
assert (
|
||||
IdMaker([], [], None, None, None, None, None)._idval(val, "a", 6)
|
||||
== expected
|
||||
)
|
||||
|
||||
def test_unicode_idval_with_config(self) -> None:
|
||||
"""Unit test for expected behavior to obtain ids with
|
||||
@@ -331,7 +338,7 @@ class TestMetafunc:
|
||||
("ação", MockConfig({option: False}), "a\\xe7\\xe3o"),
|
||||
]
|
||||
for val, config, expected in values:
|
||||
actual = _idval(val, "a", 6, None, nodeid=None, config=config)
|
||||
actual = IdMaker([], [], None, None, config, None, None)._idval(val, "a", 6)
|
||||
assert actual == expected
|
||||
|
||||
def test_bytes_idval(self) -> None:
|
||||
@@ -344,7 +351,10 @@ class TestMetafunc:
|
||||
("αρά".encode(), r"\xce\xb1\xcf\x81\xce\xac"),
|
||||
]
|
||||
for val, expected in values:
|
||||
assert _idval(val, "a", 6, idfn=None, nodeid=None, config=None) == expected
|
||||
assert (
|
||||
IdMaker([], [], None, None, None, None, None)._idval(val, "a", 6)
|
||||
== expected
|
||||
)
|
||||
|
||||
def test_class_or_function_idval(self) -> None:
|
||||
"""Unit test for the expected behavior to obtain ids for parametrized
|
||||
@@ -358,7 +368,10 @@ class TestMetafunc:
|
||||
|
||||
values = [(TestClass, "TestClass"), (test_function, "test_function")]
|
||||
for val, expected in values:
|
||||
assert _idval(val, "a", 6, None, nodeid=None, config=None) == expected
|
||||
assert (
|
||||
IdMaker([], [], None, None, None, None, None)._idval(val, "a", 6)
|
||||
== expected
|
||||
)
|
||||
|
||||
def test_notset_idval(self) -> None:
|
||||
"""Test that a NOTSET value (used by an empty parameterset) generates
|
||||
@@ -366,29 +379,47 @@ class TestMetafunc:
|
||||
|
||||
Regression test for #7686.
|
||||
"""
|
||||
assert _idval(NOTSET, "a", 0, None, nodeid=None, config=None) == "a0"
|
||||
assert (
|
||||
IdMaker([], [], None, None, None, None, None)._idval(NOTSET, "a", 0) == "a0"
|
||||
)
|
||||
|
||||
def test_idmaker_autoname(self) -> None:
|
||||
"""#250"""
|
||||
result = idmaker(
|
||||
("a", "b"), [pytest.param("string", 1.0), pytest.param("st-ring", 2.0)]
|
||||
)
|
||||
result = IdMaker(
|
||||
("a", "b"),
|
||||
[pytest.param("string", 1.0), pytest.param("st-ring", 2.0)],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).make_unique_parameterset_ids()
|
||||
assert result == ["string-1.0", "st-ring-2.0"]
|
||||
|
||||
result = idmaker(
|
||||
("a", "b"), [pytest.param(object(), 1.0), pytest.param(object(), object())]
|
||||
)
|
||||
result = IdMaker(
|
||||
("a", "b"),
|
||||
[pytest.param(object(), 1.0), pytest.param(object(), object())],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).make_unique_parameterset_ids()
|
||||
assert result == ["a0-1.0", "a1-b1"]
|
||||
# unicode mixing, issue250
|
||||
result = idmaker(("a", "b"), [pytest.param({}, b"\xc3\xb4")])
|
||||
result = IdMaker(
|
||||
("a", "b"), [pytest.param({}, b"\xc3\xb4")], None, None, None, None, None
|
||||
).make_unique_parameterset_ids()
|
||||
assert result == ["a0-\\xc3\\xb4"]
|
||||
|
||||
def test_idmaker_with_bytes_regex(self) -> None:
|
||||
result = idmaker(("a"), [pytest.param(re.compile(b"foo"), 1.0)])
|
||||
result = IdMaker(
|
||||
("a"), [pytest.param(re.compile(b"foo"), 1.0)], None, None, None, None, None
|
||||
).make_unique_parameterset_ids()
|
||||
assert result == ["foo"]
|
||||
|
||||
def test_idmaker_native_strings(self) -> None:
|
||||
result = idmaker(
|
||||
result = IdMaker(
|
||||
("a", "b"),
|
||||
[
|
||||
pytest.param(1.0, -1.1),
|
||||
@@ -403,8 +434,14 @@ class TestMetafunc:
|
||||
pytest.param(tuple("eight"), (8, -8, 8)),
|
||||
pytest.param(b"\xc3\xb4", b"name"),
|
||||
pytest.param(b"\xc3\xb4", "other"),
|
||||
pytest.param(1.0j, -2.0j),
|
||||
],
|
||||
)
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).make_unique_parameterset_ids()
|
||||
assert result == [
|
||||
"1.0--1.1",
|
||||
"2--202",
|
||||
@@ -418,10 +455,11 @@ class TestMetafunc:
|
||||
"a9-b9",
|
||||
"\\xc3\\xb4-name",
|
||||
"\\xc3\\xb4-other",
|
||||
"1j-(-0-2j)",
|
||||
]
|
||||
|
||||
def test_idmaker_non_printable_characters(self) -> None:
|
||||
result = idmaker(
|
||||
result = IdMaker(
|
||||
("s", "n"),
|
||||
[
|
||||
pytest.param("\x00", 1),
|
||||
@@ -431,23 +469,35 @@ class TestMetafunc:
|
||||
pytest.param("\t", 5),
|
||||
pytest.param(b"\t", 6),
|
||||
],
|
||||
)
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).make_unique_parameterset_ids()
|
||||
assert result == ["\\x00-1", "\\x05-2", "\\x00-3", "\\x05-4", "\\t-5", "\\t-6"]
|
||||
|
||||
def test_idmaker_manual_ids_must_be_printable(self) -> None:
|
||||
result = idmaker(
|
||||
result = IdMaker(
|
||||
("s",),
|
||||
[
|
||||
pytest.param("x00", id="hello \x00"),
|
||||
pytest.param("x05", id="hello \x05"),
|
||||
],
|
||||
)
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).make_unique_parameterset_ids()
|
||||
assert result == ["hello \\x00", "hello \\x05"]
|
||||
|
||||
def test_idmaker_enum(self) -> None:
|
||||
enum = pytest.importorskip("enum")
|
||||
e = enum.Enum("Foo", "one, two")
|
||||
result = idmaker(("a", "b"), [pytest.param(e.one, e.two)])
|
||||
result = IdMaker(
|
||||
("a", "b"), [pytest.param(e.one, e.two)], None, None, None, None, None
|
||||
).make_unique_parameterset_ids()
|
||||
assert result == ["Foo.one-Foo.two"]
|
||||
|
||||
def test_idmaker_idfn(self) -> None:
|
||||
@@ -458,15 +508,19 @@ class TestMetafunc:
|
||||
return repr(val)
|
||||
return None
|
||||
|
||||
result = idmaker(
|
||||
result = IdMaker(
|
||||
("a", "b"),
|
||||
[
|
||||
pytest.param(10.0, IndexError()),
|
||||
pytest.param(20, KeyError()),
|
||||
pytest.param("three", [1, 2, 3]),
|
||||
],
|
||||
idfn=ids,
|
||||
)
|
||||
ids,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).make_unique_parameterset_ids()
|
||||
assert result == ["10.0-IndexError()", "20-KeyError()", "three-b2"]
|
||||
|
||||
def test_idmaker_idfn_unique_names(self) -> None:
|
||||
@@ -475,15 +529,19 @@ class TestMetafunc:
|
||||
def ids(val: object) -> str:
|
||||
return "a"
|
||||
|
||||
result = idmaker(
|
||||
result = IdMaker(
|
||||
("a", "b"),
|
||||
[
|
||||
pytest.param(10.0, IndexError()),
|
||||
pytest.param(20, KeyError()),
|
||||
pytest.param("three", [1, 2, 3]),
|
||||
],
|
||||
idfn=ids,
|
||||
)
|
||||
ids,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).make_unique_parameterset_ids()
|
||||
assert result == ["a-a0", "a-a1", "a-a2"]
|
||||
|
||||
def test_idmaker_with_idfn_and_config(self) -> None:
|
||||
@@ -513,12 +571,15 @@ class TestMetafunc:
|
||||
(MockConfig({option: False}), "a\\xe7\\xe3o"),
|
||||
]
|
||||
for config, expected in values:
|
||||
result = idmaker(
|
||||
result = IdMaker(
|
||||
("a",),
|
||||
[pytest.param("string")],
|
||||
idfn=lambda _: "ação",
|
||||
config=config,
|
||||
)
|
||||
lambda _: "ação",
|
||||
None,
|
||||
config,
|
||||
None,
|
||||
None,
|
||||
).make_unique_parameterset_ids()
|
||||
assert result == [expected]
|
||||
|
||||
def test_idmaker_with_ids_and_config(self) -> None:
|
||||
@@ -548,12 +609,9 @@ class TestMetafunc:
|
||||
(MockConfig({option: False}), "a\\xe7\\xe3o"),
|
||||
]
|
||||
for config, expected in values:
|
||||
result = idmaker(
|
||||
("a",),
|
||||
[pytest.param("string")],
|
||||
ids=["ação"],
|
||||
config=config,
|
||||
)
|
||||
result = IdMaker(
|
||||
("a",), [pytest.param("string")], None, ["ação"], config, None, None
|
||||
).make_unique_parameterset_ids()
|
||||
assert result == [expected]
|
||||
|
||||
def test_parametrize_ids_exception(self, pytester: Pytester) -> None:
|
||||
@@ -610,23 +668,39 @@ class TestMetafunc:
|
||||
)
|
||||
|
||||
def test_idmaker_with_ids(self) -> None:
|
||||
result = idmaker(
|
||||
("a", "b"), [pytest.param(1, 2), pytest.param(3, 4)], ids=["a", None]
|
||||
)
|
||||
result = IdMaker(
|
||||
("a", "b"),
|
||||
[pytest.param(1, 2), pytest.param(3, 4)],
|
||||
None,
|
||||
["a", None],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).make_unique_parameterset_ids()
|
||||
assert result == ["a", "3-4"]
|
||||
|
||||
def test_idmaker_with_paramset_id(self) -> None:
|
||||
result = idmaker(
|
||||
result = IdMaker(
|
||||
("a", "b"),
|
||||
[pytest.param(1, 2, id="me"), pytest.param(3, 4, id="you")],
|
||||
ids=["a", None],
|
||||
)
|
||||
None,
|
||||
["a", None],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).make_unique_parameterset_ids()
|
||||
assert result == ["me", "you"]
|
||||
|
||||
def test_idmaker_with_ids_unique_names(self) -> None:
|
||||
result = idmaker(
|
||||
("a"), map(pytest.param, [1, 2, 3, 4, 5]), ids=["a", "a", "b", "c", "b"]
|
||||
)
|
||||
result = IdMaker(
|
||||
("a"),
|
||||
list(map(pytest.param, [1, 2, 3, 4, 5])),
|
||||
None,
|
||||
["a", "a", "b", "c", "b"],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).make_unique_parameterset_ids()
|
||||
assert result == ["a0", "a1", "b0", "c", "b1"]
|
||||
|
||||
def test_parametrize_indirect(self) -> None:
|
||||
@@ -692,9 +766,8 @@ class TestMetafunc:
|
||||
"""
|
||||
#714
|
||||
Test parametrization with 'indirect' parameter applied on
|
||||
particular arguments. As y is is direct, its value should
|
||||
be used directly rather than being passed to the fixture
|
||||
y.
|
||||
particular arguments. As y is direct, its value should
|
||||
be used directly rather than being passed to the fixture y.
|
||||
|
||||
:param pytester: the instance of Pytester class, a temporary
|
||||
test directory.
|
||||
@@ -1266,7 +1339,7 @@ class TestMetafuncFunctional:
|
||||
"""
|
||||
import pytest
|
||||
|
||||
@pytest.mark.parametrize("x, expected", [(1, 2), (3, 4), (5, 6)], ids=(None, 2, type))
|
||||
@pytest.mark.parametrize("x, expected", [(1, 2), (3, 4), (5, 6)], ids=(None, 2, OSError()))
|
||||
def test_ids_numbers(x,expected):
|
||||
assert x * 2 == expected
|
||||
"""
|
||||
@@ -1274,8 +1347,8 @@ class TestMetafuncFunctional:
|
||||
result = pytester.runpytest()
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"In test_ids_numbers: ids must be list of string/float/int/bool,"
|
||||
" found: <class 'type'> (type: <class 'type'>) at index 2"
|
||||
"In test_ids_numbers: ids contains unsupported value OSError() (type: <class 'OSError'>) at index 2. "
|
||||
"Supported types are: str, bytes, int, float, complex, bool, enum, regex or anything with a __name__."
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -19,6 +19,16 @@ class TestRaises:
|
||||
excinfo = pytest.raises(ValueError, int, "hello")
|
||||
assert "invalid literal" in str(excinfo.value)
|
||||
|
||||
def test_raises_does_not_allow_none(self):
|
||||
with pytest.raises(ValueError, match="Expected an exception type or"):
|
||||
# We're testing that this invalid usage gives a helpful error,
|
||||
# so we can ignore Mypy telling us that None is invalid.
|
||||
pytest.raises(expected_exception=None) # type: ignore
|
||||
|
||||
def test_raises_does_not_allow_empty_tuple(self):
|
||||
with pytest.raises(ValueError, match="Expected an exception type or"):
|
||||
pytest.raises(expected_exception=())
|
||||
|
||||
def test_raises_callable_no_exception(self) -> None:
|
||||
class A:
|
||||
def __call__(self):
|
||||
@@ -82,13 +92,9 @@ class TestRaises:
|
||||
def test_does_not_raise(self, pytester: Pytester) -> None:
|
||||
pytester.makepyfile(
|
||||
"""
|
||||
from contextlib import contextmanager
|
||||
from contextlib import nullcontext as does_not_raise
|
||||
import pytest
|
||||
|
||||
@contextmanager
|
||||
def does_not_raise():
|
||||
yield
|
||||
|
||||
@pytest.mark.parametrize('example_input,expectation', [
|
||||
(3, does_not_raise()),
|
||||
(2, does_not_raise()),
|
||||
@@ -107,13 +113,9 @@ class TestRaises:
|
||||
def test_does_not_raise_does_raise(self, pytester: Pytester) -> None:
|
||||
pytester.makepyfile(
|
||||
"""
|
||||
from contextlib import contextmanager
|
||||
from contextlib import nullcontext as does_not_raise
|
||||
import pytest
|
||||
|
||||
@contextmanager
|
||||
def does_not_raise():
|
||||
yield
|
||||
|
||||
@pytest.mark.parametrize('example_input,expectation', [
|
||||
(0, does_not_raise()),
|
||||
(1, pytest.raises(ZeroDivisionError)),
|
||||
@@ -144,7 +146,7 @@ class TestRaises:
|
||||
try:
|
||||
pytest.raises(ValueError, int, "0")
|
||||
except pytest.fail.Exception as e:
|
||||
assert e.msg == "DID NOT RAISE {}".format(repr(ValueError))
|
||||
assert e.msg == f"DID NOT RAISE {repr(ValueError)}"
|
||||
else:
|
||||
assert False, "Expected pytest.raises.Exception"
|
||||
|
||||
@@ -152,7 +154,7 @@ class TestRaises:
|
||||
with pytest.raises(ValueError):
|
||||
pass
|
||||
except pytest.fail.Exception as e:
|
||||
assert e.msg == "DID NOT RAISE {}".format(repr(ValueError))
|
||||
assert e.msg == f"DID NOT RAISE {repr(ValueError)}"
|
||||
else:
|
||||
assert False, "Expected pytest.raises.Exception"
|
||||
|
||||
@@ -191,10 +193,12 @@ class TestRaises:
|
||||
int("asdf")
|
||||
|
||||
msg = "with base 16"
|
||||
expr = "Regex pattern {!r} does not match \"invalid literal for int() with base 10: 'asdf'\".".format(
|
||||
msg
|
||||
expr = (
|
||||
"Regex pattern did not match.\n"
|
||||
f" Regex: {msg!r}\n"
|
||||
" Input: \"invalid literal for int() with base 10: 'asdf'\""
|
||||
)
|
||||
with pytest.raises(AssertionError, match=re.escape(expr)):
|
||||
with pytest.raises(AssertionError, match="(?m)" + re.escape(expr)):
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
int("asdf", base=10)
|
||||
|
||||
@@ -217,7 +221,7 @@ class TestRaises:
|
||||
with pytest.raises(AssertionError, match="'foo"):
|
||||
raise AssertionError("'bar")
|
||||
(msg,) = excinfo.value.args
|
||||
assert msg == 'Regex pattern "\'foo" does not match "\'bar".'
|
||||
assert msg == '''Regex pattern did not match.\n Regex: "'foo"\n Input: "'bar"'''
|
||||
|
||||
def test_match_failure_exact_string_message(self):
|
||||
message = "Oh here is a message with (42) numbers in parameters"
|
||||
@@ -226,9 +230,10 @@ class TestRaises:
|
||||
raise AssertionError(message)
|
||||
(msg,) = excinfo.value.args
|
||||
assert msg == (
|
||||
"Regex pattern 'Oh here is a message with (42) numbers in "
|
||||
"parameters' does not match 'Oh here is a message with (42) "
|
||||
"numbers in parameters'. Did you mean to `re.escape()` the regex?"
|
||||
"Regex pattern did not match.\n"
|
||||
" Regex: 'Oh here is a message with (42) numbers in parameters'\n"
|
||||
" Input: 'Oh here is a message with (42) numbers in parameters'\n"
|
||||
" Did you mean to `re.escape()` the regex?"
|
||||
)
|
||||
|
||||
def test_raises_match_wrong_type(self):
|
||||
|
||||
@@ -29,7 +29,7 @@ def test_fixtures_in_module(pytester: Pytester) -> None:
|
||||
[
|
||||
"*fixtures used by test_arg1*",
|
||||
"*(test_fixtures_in_module.py:9)*",
|
||||
"arg1",
|
||||
"arg1 -- test_fixtures_in_module.py:6",
|
||||
" arg1 docstring",
|
||||
]
|
||||
)
|
||||
@@ -68,17 +68,16 @@ def test_fixtures_in_conftest(pytester: Pytester) -> None:
|
||||
[
|
||||
"*fixtures used by test_arg2*",
|
||||
"*(test_fixtures_in_conftest.py:2)*",
|
||||
"arg2",
|
||||
"arg2 -- conftest.py:6",
|
||||
" arg2 docstring",
|
||||
"*fixtures used by test_arg3*",
|
||||
"*(test_fixtures_in_conftest.py:4)*",
|
||||
"arg1",
|
||||
"arg1 -- conftest.py:3",
|
||||
" arg1 docstring",
|
||||
"arg2",
|
||||
"arg2 -- conftest.py:6",
|
||||
" arg2 docstring",
|
||||
"arg3",
|
||||
"arg3 -- conftest.py:9",
|
||||
" arg3",
|
||||
" docstring",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -112,9 +111,9 @@ def test_should_show_fixtures_used_by_test(pytester: Pytester) -> None:
|
||||
[
|
||||
"*fixtures used by test_args*",
|
||||
"*(test_should_show_fixtures_used_by_test.py:6)*",
|
||||
"arg1",
|
||||
"arg1 -- test_should_show_fixtures_used_by_test.py:3",
|
||||
" arg1 from testmodule",
|
||||
"arg2",
|
||||
"arg2 -- conftest.py:6",
|
||||
" arg2 from conftest",
|
||||
]
|
||||
)
|
||||
@@ -181,3 +180,75 @@ def test_doctest_items(pytester: Pytester) -> None:
|
||||
assert result.ret == 0
|
||||
|
||||
result.stdout.fnmatch_lines(["*collected 2 items*"])
|
||||
|
||||
|
||||
def test_multiline_docstring_in_module(pytester: Pytester) -> None:
|
||||
p = pytester.makepyfile(
|
||||
'''
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def arg1():
|
||||
"""Docstring content that spans across multiple lines,
|
||||
through second line,
|
||||
and through third line.
|
||||
|
||||
Docstring content that extends into a second paragraph.
|
||||
|
||||
Docstring content that extends into a third paragraph.
|
||||
"""
|
||||
def test_arg1(arg1):
|
||||
pass
|
||||
'''
|
||||
)
|
||||
|
||||
result = pytester.runpytest("--fixtures-per-test", p)
|
||||
assert result.ret == 0
|
||||
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*fixtures used by test_arg1*",
|
||||
"*(test_multiline_docstring_in_module.py:13)*",
|
||||
"arg1 -- test_multiline_docstring_in_module.py:3",
|
||||
" Docstring content that spans across multiple lines,",
|
||||
" through second line,",
|
||||
" and through third line.",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def test_verbose_include_multiline_docstring(pytester: Pytester) -> None:
|
||||
p = pytester.makepyfile(
|
||||
'''
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def arg1():
|
||||
"""Docstring content that spans across multiple lines,
|
||||
through second line,
|
||||
and through third line.
|
||||
|
||||
Docstring content that extends into a second paragraph.
|
||||
|
||||
Docstring content that extends into a third paragraph.
|
||||
"""
|
||||
def test_arg1(arg1):
|
||||
pass
|
||||
'''
|
||||
)
|
||||
|
||||
result = pytester.runpytest("--fixtures-per-test", "-v", p)
|
||||
assert result.ret == 0
|
||||
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*fixtures used by test_arg1*",
|
||||
"*(test_verbose_include_multiline_docstring.py:13)*",
|
||||
"arg1 -- test_verbose_include_multiline_docstring.py:3",
|
||||
" Docstring content that spans across multiple lines,",
|
||||
" through second line,",
|
||||
" and through third line.",
|
||||
" ",
|
||||
" Docstring content that extends into a second paragraph.",
|
||||
" ",
|
||||
" Docstring content that extends into a third paragraph.",
|
||||
]
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user