Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pytest-lazy-fixture doesn't work for deep nesting #35

Open
bigbZik opened this issue Feb 11, 2019 · 5 comments
Open

pytest-lazy-fixture doesn't work for deep nesting #35

bigbZik opened this issue Feb 11, 2019 · 5 comments

Comments

@bigbZik
Copy link

bigbZik commented Feb 11, 2019

pytest-lazy-fixture doesn't work for deep nesting

platform linux -- Python 3.5.2, pytest-3.10.1, py-1.7.0, pluggy-0.8.1
plugins: timeout-1.3.0, celery-4.2.1, repeat-0.7.0, reportportal-1.0.4, assume-1.2, lazy-fixture-0.5.1

Example:

import pytest

@pytest.fixture(scope='function')
def fixt1():
    return 111

@pytest.fixture(scope='function')
def fixt2():
    return pytest.lazy_fixture("fixt1")

@pytest.fixture(scope='function')
def fixt3():
    return pytest.lazy_fixture("fixt2")

def test(fixt3):
    print("Value in test: {}".format(fixt3))

Returns:

Value in test: <LazyFixture "fixt1">

Should be:

Value in test: 111
@TvoroG
Copy link
Owner

TvoroG commented Feb 12, 2019

Hi, @bigbZik! Thanks for reporting the issue, but currently pytest.lazy_fixture can only be used inside of @pytest.mark.parametrize() and @pytest.fixture(params=[]).

@YannickJadoul
Copy link
Contributor

YannickJadoul commented Feb 12, 2019

@bigbZik The main problem to get such a thing working on the long term is that pytest has two logical phases: first, all tests (with different parametrizations) are collected; and only then all tests (and necessary fixtures) are executed.

So in your example, only when fixt3 gets executed, pytest.lazy_fixture("fixt2") gets returned. So if fixt2 would be parametrized, there'd be no way for test to be instantiated multiple times during test collection.

But in this case, what's the problem with these two alternatives?

import pytest

@pytest.fixture(scope='function')
def fixt1():
    return 111

@pytest.fixture(scope='function')
def fixt2(fixt1):
    return fixt1

@pytest.fixture(scope='function')
def fixt3(fixt2):
    return fixt2

def test(fixt3):
    print("Value in test: {}".format(fixt3))

or

import pytest

@pytest.fixture(scope='function')
def fixt1():
    return 111

@pytest.fixture(scope='function', params=[pytest.lazy_fixture('fixt1')])
def fixt2(request):
    return request.param

@pytest.fixture(scope='function', params=[pytest.lazy_fixture('fixt2')])
def fixt3(request):
    return request.param

def test(fixt3):
    print("Value in test: {}".format(fixt3))

@bigbZik
Copy link
Author

bigbZik commented Feb 12, 2019

I'm using pytest.lazy_fixture in if statement to call different fixtures depending on some condition:

Something like this:

@pytest.fixture(scope='function')
def fixt1():
    # obj.create()
    obj = 111
    yield obj
    # obj.delete

@pytest.fixture(scope='function')
def fixt2():
    # obj.create()
    obj = 222
    yield obj
    # obj.delete

@pytest.fixture(scope='function')
def fixt3(selector):
    if selector == 1:
        return pytest.lazy_fixture("fixt1")
    else:
        return pytest.lazy_fixture("fixt2")

@pytest.mark.parametrize("selector", [1, 2])
def test(fixt3, selector):
    #fixt3.run()
    print("Value in test: {} {}".format(selector, fixt3))

@YannickJadoul
Copy link
Contributor

@bigbZik OK, yes, makes sense that your actual use case was different. Would this work instead, in your larger example?

@pytest.fixture(scope='function')
def fixt1():
    # obj.create()
    obj = 111
    yield obj
    # obj.delete

@pytest.fixture(scope='function')
def fixt2():
    # obj.create()
    obj = 222
    yield obj
    # obj.delete

@pytest.fixture(scope='function')
def fixt3(selected):
    return selected

@pytest.mark.parametrize("selector,selected", [(1, pytest.lazy_fixture("fixt1")), (2, pytest.lazy_fixture("fixt2"))])
def test(fixt3, selector):
    #fixt3.run()
    print("Value in test: {} {}".format(selector, fixt3))

Again, the main point is that you cannot really dynamically ask for more fixtures in pytest when the tests and fixtures are running. But in your example, this doesn't seem to be the case anyway, because you do know the selector parameters before and know how many tests you need to instantiate.

@smarie
Copy link

smarie commented Jun 12, 2020

This is a relatively old post, but pytest-cases might be a solution for you. Its parametrize_plus decorator supports referencing fixtures that are themselves parametrized, with fixture_ref.

In short pytest-cases provides a generalization of the great pytest-lazy-fixture to support more complex use-cases such as parametrization of lazy fixtures (and many other things such as fixture unions, etc.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants