Move fixtures.py::add_funcarg_pseudo_fixture_def to Metafunc.parametrize (#11220)

To remove fixtures.py::add_funcargs_pseudo_fixture_def and add its logic
i.e. registering funcargs as params and making corresponding fixturedefs,
right to Metafunc.parametrize in which parametrization takes place.

To remove funcargs from metafunc attributes as we populate metafunc
params and make pseudo fixturedefs simultaneously and there's no need to
keep funcargs separately.
This commit is contained in:
Sadra Barikbin
2023-08-09 20:43:45 +03:30
committed by GitHub
parent b2186e2455
commit 09b78737a5
4 changed files with 167 additions and 130 deletions

View File

@@ -23,6 +23,7 @@ from _pytest.compat import getfuncargnames
from _pytest.compat import NOTSET
from _pytest.outcomes import fail
from _pytest.pytester import Pytester
from _pytest.python import Function
from _pytest.python import IdMaker
from _pytest.scope import Scope
@@ -33,11 +34,19 @@ class TestMetafunc:
# on the funcarg level, so we don't need a full blown
# initialization.
class FuncFixtureInfoMock:
name2fixturedefs = None
name2fixturedefs: Dict[str, List[fixtures.FixtureDef[object]]] = {}
def __init__(self, names):
self.names_closure = names
@dataclasses.dataclass
class FixtureManagerMock:
config: Any
@dataclasses.dataclass
class SessionMock:
_fixturemanager: FixtureManagerMock
@dataclasses.dataclass
class DefinitionMock(python.FunctionDefinition):
_nodeid: str
@@ -46,6 +55,8 @@ class TestMetafunc:
names = getfuncargnames(func)
fixtureinfo: Any = FuncFixtureInfoMock(names)
definition: Any = DefinitionMock._create(obj=func, _nodeid="mock::nodeid")
definition._fixtureinfo = fixtureinfo
definition.session = SessionMock(FixtureManagerMock({}))
return python.Metafunc(definition, fixtureinfo, config, _ispytest=True)
def test_no_funcargs(self) -> None:
@@ -98,7 +109,7 @@ class TestMetafunc:
# When the input is an iterator, only len(args) are taken,
# so the bad Exc isn't reached.
metafunc.parametrize("x", [1, 2], ids=gen()) # type: ignore[arg-type]
assert [(x.funcargs, x.id) for x in metafunc._calls] == [
assert [(x.params, x.id) for x in metafunc._calls] == [
({"x": 1}, "0"),
({"x": 2}, "2"),
]
@@ -714,8 +725,6 @@ class TestMetafunc:
metafunc.parametrize("x", [1], indirect=True)
metafunc.parametrize("y", [2, 3], indirect=True)
assert len(metafunc._calls) == 2
assert metafunc._calls[0].funcargs == {}
assert metafunc._calls[1].funcargs == {}
assert metafunc._calls[0].params == dict(x=1, y=2)
assert metafunc._calls[1].params == dict(x=1, y=3)
@@ -727,8 +736,10 @@ class TestMetafunc:
metafunc = self.Metafunc(func)
metafunc.parametrize("x, y", [("a", "b")], indirect=["x"])
assert metafunc._calls[0].funcargs == dict(y="b")
assert metafunc._calls[0].params == dict(x="a")
assert metafunc._calls[0].params == dict(x="a", y="b")
# Since `y` is a direct parameter, its pseudo-fixture would
# be registered.
assert list(metafunc._arg2fixturedefs.keys()) == ["y"]
def test_parametrize_indirect_list_all(self) -> None:
"""#714"""
@@ -738,8 +749,8 @@ class TestMetafunc:
metafunc = self.Metafunc(func)
metafunc.parametrize("x, y", [("a", "b")], indirect=["x", "y"])
assert metafunc._calls[0].funcargs == {}
assert metafunc._calls[0].params == dict(x="a", y="b")
assert list(metafunc._arg2fixturedefs.keys()) == []
def test_parametrize_indirect_list_empty(self) -> None:
"""#714"""
@@ -749,8 +760,8 @@ class TestMetafunc:
metafunc = self.Metafunc(func)
metafunc.parametrize("x, y", [("a", "b")], indirect=[])
assert metafunc._calls[0].funcargs == dict(x="a", y="b")
assert metafunc._calls[0].params == {}
assert metafunc._calls[0].params == dict(x="a", y="b")
assert list(metafunc._arg2fixturedefs.keys()) == ["x", "y"]
def test_parametrize_indirect_wrong_type(self) -> None:
def func(x, y):
@@ -944,9 +955,9 @@ class TestMetafunc:
metafunc = self.Metafunc(lambda x: None)
metafunc.parametrize("x", [1, 2])
assert len(metafunc._calls) == 2
assert metafunc._calls[0].funcargs == dict(x=1)
assert metafunc._calls[0].params == dict(x=1)
assert metafunc._calls[0].id == "1"
assert metafunc._calls[1].funcargs == dict(x=2)
assert metafunc._calls[1].params == dict(x=2)
assert metafunc._calls[1].id == "2"
def test_parametrize_onearg_indirect(self) -> None:
@@ -961,11 +972,45 @@ class TestMetafunc:
metafunc = self.Metafunc(lambda x, y: None)
metafunc.parametrize(("x", "y"), [(1, 2), (3, 4)])
assert len(metafunc._calls) == 2
assert metafunc._calls[0].funcargs == dict(x=1, y=2)
assert metafunc._calls[0].params == dict(x=1, y=2)
assert metafunc._calls[0].id == "1-2"
assert metafunc._calls[1].funcargs == dict(x=3, y=4)
assert metafunc._calls[1].params == dict(x=3, y=4)
assert metafunc._calls[1].id == "3-4"
def test_high_scoped_parametrize_reordering(self, pytester: Pytester) -> None:
pytester.makepyfile(
"""
import pytest
@pytest.mark.parametrize("arg2", [3, 4])
@pytest.mark.parametrize("arg1", [0, 1, 2], scope='module')
def test1(arg1, arg2):
pass
def test2():
pass
@pytest.mark.parametrize("arg1", [0, 1, 2], scope='module')
def test3(arg1):
pass
"""
)
result = pytester.runpytest("--collect-only")
result.stdout.re_match_lines(
[
r" <Function test1\[0-3\]>",
r" <Function test1\[0-4\]>",
r" <Function test3\[0\]>",
r" <Function test1\[1-3\]>",
r" <Function test1\[1-4\]>",
r" <Function test3\[1\]>",
r" <Function test1\[2-3\]>",
r" <Function test1\[2-4\]>",
r" <Function test3\[2\]>",
r" <Function test2>",
]
)
def test_parametrize_multiple_times(self, pytester: Pytester) -> None:
pytester.makepyfile(
"""
@@ -1505,6 +1550,38 @@ class TestMetafuncFunctional:
result = pytester.runpytest()
assert result.ret == 0
def test_parametrize_module_level_test_with_class_scope(
self, pytester: Pytester
) -> None:
"""
Test that a class-scoped parametrization without a corresponding `Class`
gets module scope, i.e. we only create a single FixtureDef for it per module.
"""
module = pytester.makepyfile(
"""
import pytest
@pytest.mark.parametrize("x", [0, 1], scope="class")
def test_1(x):
pass
@pytest.mark.parametrize("x", [1, 2], scope="module")
def test_2(x):
pass
"""
)
test_1_0, _, test_2_0, _ = pytester.genitems((pytester.getmodulecol(module),))
assert isinstance(test_1_0, Function)
assert test_1_0.name == "test_1[0]"
test_1_fixture_x = test_1_0._fixtureinfo.name2fixturedefs["x"][-1]
assert isinstance(test_2_0, Function)
assert test_2_0.name == "test_2[1]"
test_2_fixture_x = test_2_0._fixtureinfo.name2fixturedefs["x"][-1]
assert test_1_fixture_x is test_2_fixture_x
def test_reordering_with_scopeless_and_just_indirect_parametrization(
self, pytester: Pytester
) -> None: