add plan for better fixture implementation, an xfailing test
and a slight refactoring of Metafunc tests/creation
This commit is contained in:
parent
f5d796b093
commit
4541456a96
|
@ -0,0 +1,40 @@
|
|||
|
||||
internal fixture mechanics
|
||||
----------------------------
|
||||
|
||||
fixture mechanics are contained in the python.py plugin.
|
||||
|
||||
pytest fixtures are stored and managed from the FixtureManager (fm) class.
|
||||
Upon collection fm.parsefactories() is called multiple times to parse
|
||||
fixture function definitions into FixtureDef objects.
|
||||
|
||||
During collection of test functions, metafunc needs to grow a "fixturenames"
|
||||
list so that pytest_generate_tests() hooks can check it. These fixturenames
|
||||
are a closure of all known fixtures to be used for this function:
|
||||
|
||||
- ini-defined usefixtures
|
||||
- autouse-marked fixtures along the collection chain up from the function
|
||||
- usefixtures markers at module/class/function level
|
||||
- test function funcargs
|
||||
|
||||
The latter list (without the closure) is also called "_initialfixtures"
|
||||
and will be used during the test setup phase.
|
||||
|
||||
Upon the test-setup phases initialfixtures are instantiated which
|
||||
will subsequently create the full fixture closure (as was computed in
|
||||
metafunc.fixturenames during collection). As fixture functions
|
||||
can invoke request.getfuncargvalue() the actual closure may be even
|
||||
bigger.
|
||||
|
||||
object model
|
||||
---------------------
|
||||
|
||||
As part of the metafunc-protocol parents of Function nodes get a
|
||||
FuncFixtureInfos() object, containing helping/caching for
|
||||
for a function. .getfixtureinfo(func) returns a FixtureInfo with these
|
||||
attributes:
|
||||
|
||||
- names_initial: list of initial fixture names (see above)
|
||||
- names_closure: closure of all fixture names
|
||||
- name2fixturedeflist: for creating the value
|
||||
|
|
@ -309,8 +309,7 @@ class PyCollector(PyobjMixin, pytest.Collector):
|
|||
clscol = self.getparent(Class)
|
||||
cls = clscol and clscol.obj or None
|
||||
transfer_markers(funcobj, cls, module)
|
||||
metafunc = Metafunc(funcobj, parentnode=self, config=self.config,
|
||||
cls=cls, module=module)
|
||||
metafunc = Metafunc(funcobj, parentnode=self, cls=cls, module=module)
|
||||
gentesthook = self.config.hook.pytest_generate_tests
|
||||
extra = [module]
|
||||
if cls is not None:
|
||||
|
@ -612,25 +611,23 @@ class FuncargnamesCompatAttr:
|
|||
return self.fixturenames
|
||||
|
||||
class Metafunc(FuncargnamesCompatAttr):
|
||||
def __init__(self, function, config=None, cls=None, module=None,
|
||||
parentnode=None):
|
||||
self.config = config
|
||||
def __init__(self, function, parentnode, cls=None, module=None):
|
||||
self.config = parentnode.config
|
||||
self.module = module
|
||||
self.function = function
|
||||
self.parentnode = parentnode
|
||||
self._parentid = getattr(parentnode, "nodeid", "")
|
||||
argnames = getfuncargnames(function, startindex=int(cls is not None))
|
||||
if parentnode is not None:
|
||||
try:
|
||||
fm = parentnode.session._fixturemanager
|
||||
except AttributeError:
|
||||
self.fixturenames = argnames
|
||||
else:
|
||||
self.fixturenames, self._arg2fixturedeflist = fm.getfixtureclosure(
|
||||
argnames, parentnode)
|
||||
else:
|
||||
self.fixturenames = argnames
|
||||
self.cls = cls
|
||||
self.module = module
|
||||
self._calls = []
|
||||
self._ids = py.builtin.set()
|
||||
self._arg2scopenum = {}
|
||||
|
||||
def parametrize(self, argnames, argvalues, indirect=False, ids=None,
|
||||
scope=None):
|
||||
|
|
|
@ -284,22 +284,22 @@ class TestFunction:
|
|||
pass
|
||||
def func2():
|
||||
pass
|
||||
f1 = pytest.Function(name="name", config=config,
|
||||
args=(1,), callobj=func1, session=session)
|
||||
f1 = pytest.Function(name="name", parent=session, config=config,
|
||||
args=(1,), callobj=func1)
|
||||
f2 = pytest.Function(name="name",config=config,
|
||||
args=(1,), callobj=func2, session=session)
|
||||
args=(1,), callobj=func2, parent=session)
|
||||
assert not f1 == f2
|
||||
assert f1 != f2
|
||||
f3 = pytest.Function(name="name", config=config,
|
||||
args=(1,2), callobj=func2, session=session)
|
||||
f3 = pytest.Function(name="name", parent=session, config=config,
|
||||
args=(1,2), callobj=func2)
|
||||
assert not f3 == f2
|
||||
assert f3 != f2
|
||||
|
||||
assert not f3 == f1
|
||||
assert f3 != f1
|
||||
|
||||
f1_b = pytest.Function(name="name", config=config,
|
||||
args=(1,), callobj=func1, session=session)
|
||||
f1_b = pytest.Function(name="name", parent=session, config=config,
|
||||
args=(1,), callobj=func1)
|
||||
assert f1 == f1_b
|
||||
assert not f1 != f1_b
|
||||
|
||||
|
@ -909,15 +909,20 @@ class TestRequestCachedSetup:
|
|||
])
|
||||
|
||||
class TestMetafunc:
|
||||
def Metafunc(self, func):
|
||||
class parent:
|
||||
config = None
|
||||
return funcargs.Metafunc(func, parentnode=parent)
|
||||
|
||||
def test_no_funcargs(self, testdir):
|
||||
def function(): pass
|
||||
metafunc = funcargs.Metafunc(function)
|
||||
metafunc = self.Metafunc(function)
|
||||
assert not metafunc.fixturenames
|
||||
repr(metafunc._calls)
|
||||
|
||||
def test_function_basic(self):
|
||||
def func(arg1, arg2="qwe"): pass
|
||||
metafunc = funcargs.Metafunc(func)
|
||||
metafunc = self.Metafunc(func)
|
||||
assert len(metafunc.fixturenames) == 1
|
||||
assert 'arg1' in metafunc.fixturenames
|
||||
assert metafunc.function is func
|
||||
|
@ -925,7 +930,7 @@ class TestMetafunc:
|
|||
|
||||
def test_addcall_no_args(self):
|
||||
def func(arg1): pass
|
||||
metafunc = funcargs.Metafunc(func)
|
||||
metafunc = self.Metafunc(func)
|
||||
metafunc.addcall()
|
||||
assert len(metafunc._calls) == 1
|
||||
call = metafunc._calls[0]
|
||||
|
@ -934,7 +939,7 @@ class TestMetafunc:
|
|||
|
||||
def test_addcall_id(self):
|
||||
def func(arg1): pass
|
||||
metafunc = funcargs.Metafunc(func)
|
||||
metafunc = self.Metafunc(func)
|
||||
pytest.raises(ValueError, "metafunc.addcall(id=None)")
|
||||
|
||||
metafunc.addcall(id=1)
|
||||
|
@ -947,7 +952,7 @@ class TestMetafunc:
|
|||
|
||||
def test_addcall_param(self):
|
||||
def func(arg1): pass
|
||||
metafunc = funcargs.Metafunc(func)
|
||||
metafunc = self.Metafunc(func)
|
||||
class obj: pass
|
||||
metafunc.addcall(param=obj)
|
||||
metafunc.addcall(param=obj)
|
||||
|
@ -959,7 +964,7 @@ class TestMetafunc:
|
|||
|
||||
def test_addcall_funcargs(self):
|
||||
def func(x): pass
|
||||
metafunc = funcargs.Metafunc(func)
|
||||
metafunc = self.Metafunc(func)
|
||||
class obj: pass
|
||||
metafunc.addcall(funcargs={"x": 2})
|
||||
metafunc.addcall(funcargs={"x": 3})
|
||||
|
@ -971,7 +976,7 @@ class TestMetafunc:
|
|||
|
||||
def test_parametrize_error(self):
|
||||
def func(x, y): pass
|
||||
metafunc = funcargs.Metafunc(func)
|
||||
metafunc = self.Metafunc(func)
|
||||
metafunc.parametrize("x", [1,2])
|
||||
pytest.raises(ValueError, lambda: metafunc.parametrize("x", [5,6]))
|
||||
pytest.raises(ValueError, lambda: metafunc.parametrize("x", [5,6]))
|
||||
|
@ -981,7 +986,7 @@ class TestMetafunc:
|
|||
|
||||
def test_parametrize_and_id(self):
|
||||
def func(x, y): pass
|
||||
metafunc = funcargs.Metafunc(func)
|
||||
metafunc = self.Metafunc(func)
|
||||
|
||||
metafunc.parametrize("x", [1,2], ids=['basic', 'advanced'])
|
||||
metafunc.parametrize("y", ["abc", "def"])
|
||||
|
@ -990,7 +995,7 @@ class TestMetafunc:
|
|||
|
||||
def test_parametrize_with_userobjects(self):
|
||||
def func(x, y): pass
|
||||
metafunc = funcargs.Metafunc(func)
|
||||
metafunc = self.Metafunc(func)
|
||||
class A:
|
||||
pass
|
||||
metafunc.parametrize("x", [A(), A()])
|
||||
|
@ -1002,7 +1007,7 @@ class TestMetafunc:
|
|||
|
||||
def test_addcall_and_parametrize(self):
|
||||
def func(x, y): pass
|
||||
metafunc = funcargs.Metafunc(func)
|
||||
metafunc = self.Metafunc(func)
|
||||
metafunc.addcall({'x': 1})
|
||||
metafunc.parametrize('y', [2,3])
|
||||
assert len(metafunc._calls) == 2
|
||||
|
@ -1013,7 +1018,7 @@ class TestMetafunc:
|
|||
|
||||
def test_parametrize_indirect(self):
|
||||
def func(x, y): pass
|
||||
metafunc = funcargs.Metafunc(func)
|
||||
metafunc = self.Metafunc(func)
|
||||
metafunc.parametrize('x', [1], indirect=True)
|
||||
metafunc.parametrize('y', [2,3], indirect=True)
|
||||
metafunc.parametrize('unnamed', [1], indirect=True)
|
||||
|
@ -1025,7 +1030,7 @@ class TestMetafunc:
|
|||
|
||||
def test_addcalls_and_parametrize_indirect(self):
|
||||
def func(x, y): pass
|
||||
metafunc = funcargs.Metafunc(func)
|
||||
metafunc = self.Metafunc(func)
|
||||
metafunc.addcall(param="123")
|
||||
metafunc.parametrize('x', [1], indirect=True)
|
||||
metafunc.parametrize('y', [2,3], indirect=True)
|
||||
|
@ -1038,7 +1043,6 @@ class TestMetafunc:
|
|||
def test_parametrize_functional(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
def pytest_generate_tests(metafunc):
|
||||
assert "test_parametrize_functional" in metafunc._parentid
|
||||
metafunc.parametrize('x', [1,2], indirect=True)
|
||||
metafunc.parametrize('y', [2])
|
||||
def pytest_funcarg__x(request):
|
||||
|
@ -1058,7 +1062,7 @@ class TestMetafunc:
|
|||
])
|
||||
|
||||
def test_parametrize_onearg(self):
|
||||
metafunc = funcargs.Metafunc(lambda x: None)
|
||||
metafunc = self.Metafunc(lambda x: None)
|
||||
metafunc.parametrize("x", [1,2])
|
||||
assert len(metafunc._calls) == 2
|
||||
assert metafunc._calls[0].funcargs == dict(x=1)
|
||||
|
@ -1067,7 +1071,7 @@ class TestMetafunc:
|
|||
assert metafunc._calls[1].id == "2"
|
||||
|
||||
def test_parametrize_onearg_indirect(self):
|
||||
metafunc = funcargs.Metafunc(lambda x: None)
|
||||
metafunc = self.Metafunc(lambda x: None)
|
||||
metafunc.parametrize("x", [1,2], indirect=True)
|
||||
assert metafunc._calls[0].params == dict(x=1)
|
||||
assert metafunc._calls[0].id == "1"
|
||||
|
@ -1075,7 +1079,7 @@ class TestMetafunc:
|
|||
assert metafunc._calls[1].id == "2"
|
||||
|
||||
def test_parametrize_twoargs(self):
|
||||
metafunc = funcargs.Metafunc(lambda x,y: None)
|
||||
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)
|
||||
|
@ -2004,6 +2008,28 @@ class TestSetupDiscovery:
|
|||
reprec = testdir.inline_run("-s")
|
||||
reprec.assertoutcome(passed=1)
|
||||
|
||||
@pytest.mark.xfail
|
||||
def test_two_classes_separated_autouse(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
class TestA:
|
||||
l = []
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup1(self):
|
||||
self.l.append(1)
|
||||
def test_setup1(self):
|
||||
assert self.l == [1]
|
||||
class TestB:
|
||||
l = []
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup2(self):
|
||||
self.l.append(1)
|
||||
def test_setup2(self):
|
||||
assert self.l == [1]
|
||||
""")
|
||||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(passed=2)
|
||||
|
||||
def test_setup_at_classlevel(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
|
|
Loading…
Reference in New Issue