0

I am looking to parametrize a pytest function with the results from a fixture. Note: I do not want to parameterize with different fixtures as is often asked instead!

In essence, the parametrization is dependent on the test data which is read from a file that changes between test invocations.

import pytest

@pytest.fixture(scope='session')
def all_components():
    # expensive fixture
    # read from a file that changes between test invocations
    # here hard-coded to illustrate structure
    return [
        {'name': 'ABC', 'type': 1, 'my_value': 'foo'},
        {'name': 'DEF', 'type': 2, 'my_value': 'bar'},
        # ...,
        {'name': 'XYZ', 'type': 1, 'my_value': 'foobar'},
    ]


class TestSomething:
    @pytest.fixture(scope='class')
    def transformed_components(self, all_components):
        # components are transformed
        return all_components

    @pytest.fixture(scope='session')
    def filtered_component_names(self, transformed_components, request):
        # extract some subset of data from the scenario definition
        # contrived example for the sake of argument
        return [val['name'] for val in transformed_components
                if val['type'] == request.param]


    @pytest.mark.parametrize('filtered_component_names', [1], scope='class', indirect=True)
    @pytest.mark.parametrize('component_name', pytest.lazy_fixture('filtered_component_names'), scope='class')
    class TestSubSomething:
        @pytest.fixture(scope='class')
        def get_value(self, transformed_components, component_name):
            component_dict = {
                comp['name']: comp
                for comp in transformed_components
            }
            def get_value_impl(field):
                # some calculation happens here
                return component_dict[component_name][field]
            return get_value_impl
        
        def test_something1(self, get_value, component_name, filtered_component_names):
            # details of the access is abstracted away,
            # i.e. no need to specify component_name, etc.
            assert get_value('my_value') == 'bar'

        def test_somethin2(...):
            ...


    @pytest.mark.parametrize('filtered_component_names', [2], scope='class', indirect=True)
    @pytest.mark.parametrize('component_name', pytest.lazy_fixture('filtered_component_names'), scope='class')
    class TestSubSomethingElse:
        ...

This almost works. The issue is that pytest.lazy_fixture is not iterable.

Also slightly ugly is that component_name, filtered_component_names must be passed to tests even though they are required only for the fixtures. But this I could live with.

Is there a way of actually making this work?

ARF
  • 7,420
  • 8
  • 45
  • 72

1 Answers1

1

If I'm not mistaken, it seems that you want a fixture to return a list of parameters. This is not possible with pytest, by design. See this post that explains it in details :

https://stackoverflow.com/a/56865893/7262247

And this, too https://github.com/smarie/python-pytest-cases/issues/235

smarie
  • 4,568
  • 24
  • 39