Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ in ``@pytest.mark.parametrize``:
])
def test_func(arg1, arg2):
assert arg2 == 1


# Callables are accepted too:
@pytest.mark.parametrize('arg', [
lazy_fixture(lambda one: one + 9)),
])
def test_func(arg):
assert arg == 10

This can be even more useful when the fixture is itself parametrized:

Expand Down
28 changes: 23 additions & 5 deletions pytest_lazyfixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import sys
import types
from collections import defaultdict
from inspect import signature
import pytest


Expand Down Expand Up @@ -32,7 +33,7 @@ def fill(request):
if hasattr(item, 'callspec'):
for param, val in sorted_by_dependency(item.callspec.params, fixturenames):
if val is not None and is_lazy_fixture(val):
item.callspec.params[param] = request.getfixturevalue(val.name)
item.callspec.params[param] = val.evaluate(request)
elif param not in item.funcargs:
item.funcargs[param] = request.getfixturevalue(param)

Expand All @@ -44,14 +45,14 @@ def fill(request):
def pytest_fixture_setup(fixturedef, request):
val = getattr(request, 'param', None)
if is_lazy_fixture(val):
request.param = request.getfixturevalue(val.name)
request.param = val.evaluate(request)


def pytest_runtest_call(item):
if hasattr(item, 'funcargs'):
for arg, val in item.funcargs.items():
if is_lazy_fixture(val):
item.funcargs[arg] = item._request.getfixturevalue(val.name)
item.funcargs[arg] = val.evaluate(item._request)


@pytest.hookimpl(hookwrapper=True)
Expand Down Expand Up @@ -176,7 +177,7 @@ def _tree_to_list(trees, leave):


def lazy_fixture(names):
if isinstance(names, string_type):
if isinstance(names, string_type) or callable(names):
return LazyFixture(names)
else:
return [LazyFixture(name) for name in names]
Expand All @@ -188,7 +189,24 @@ def is_lazy_fixture(val):

class LazyFixture(object):
def __init__(self, name):
self.name = name
if isinstance(name, str):
self.name = name
self.fn = None
self.args = [name]
elif callable(name):
self.fn = name
params = signature(self.fn).parameters.values()
self.args = [param.name for param in params if param.kind == param.POSITIONAL_OR_KEYWORD]
self.name = "<" + name.__name__ + ":" + "-".join(self.args) + ">"
else:
raise TypeError(f"Unsupported parameter: {name!r}")

def evaluate(self, request):
if self.fn is not None:
kwargs = {arg: request.getfixturevalue(arg) for arg in self.args}
return self.fn(**kwargs)
else:
return request.getfixturevalue(self.name)

def __repr__(self):
return '<{} "{}">'.format(self.__class__.__name__, self.name)
Expand Down
52 changes: 52 additions & 0 deletions tests/test_lazyfixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -936,3 +936,55 @@ def test_eq():
assert lazy_fixture("Lol") == lazy_fixture("Lol")
assert lazy_fixture("Lol") != lazy_fixture("Wut")
assert lazy_fixture("Lol") != 123

def test_lambda_function(testdir):
testdir.makepyfile("""
import pytest
from pytest_lazyfixture import lazy_fixture

@pytest.fixture
def foo():
return 1

@pytest.fixture
def bar():
return 2

@pytest.mark.parametrize(
"data",
[
lazy_fixture(lambda foo, bar: [foo, bar])
]
)
def test_the_thing(data):
assert data == [1, 2]
""")
reprec = testdir.inline_run('-vvv')
reprec.assertoutcome(passed=1)


def test_lambda_function_item_names(testdir):
items = testdir.getitems("""
import pytest
from pytest_lazyfixture import lazy_fixture

@pytest.fixture
def foo():
return 1

@pytest.fixture
def bar():
return 2

@pytest.mark.parametrize(
"data",
[
lazy_fixture(lambda foo, bar: [foo, bar])
]
)
def test_the_thing(data):
assert data == [1, 2]
assert baz == 3
""")
[item] = items
assert item.name == "test_the_thing[<<lambda>:foo-bar>]"