Search code examples
pythonpytestfixturesparametrized-testingparametrize

pytest: combine class level parametrization with class scoped fixtures


I have tests that are identical for various values, so I want to parametrize the test class. Because the code is making network calls, I also want to use class scoped fixtures.

I tried

import pytest

@pytest.mark.parametrize('fruit', ['apple', 'banana'])
class TestFruit:
    @pytest.fixture(scope='class')
    def get_fruit(self, fruit):
        print(f'Network call to GET {fruit}. This is expensive.')
        return fruit

    def test_fruit(self, get_fruit):
        print(f'The fruit is {get_fruit}.')
        assert get_fruit == 'apple'

    def test_fruit2(self, get_fruit):
        print(f'Fruit in test_fruit2 is {get_fruit}')
        assert get_fruit == 'orange'        

I want all of the tests to be run for every 'fruit' listed, and I want get_fruit to only be called once per class, not for each test.

When I run the code, I get ScopeMismatch: You tried to access the function scoped fixture endpoint with a class scoped request object

Unexpected behavior of class scoped fixture when calling a class scoped parametrized fixture in Pytest describes the issue I'm having, but I don't see a difference between the code posted in the question and the code posted in the answer.


Solution

  • One possible fix is to make the scope of get_fruit function instead of class, but I don't think you want to do this because the network call is expensive.

    Another solution is to combine the fixture and the parameterize into one:

    import pytest
    
    
    @pytest.fixture(
        scope="class",
        params=["apple", "banana"],
    )
    def get_fruit(request):
        fruit = request.param
        print(f"Network call to GET {fruit}. This is expensive.")
        return fruit
    
    
    class TestFruit:
        def test_fruit(self, get_fruit):
            print(f"The fruit is {get_fruit}.")
            assert get_fruit == "apple"
    
        def test_fruit2(self, get_fruit):
            print(f"Fruit in test_fruit2 is {get_fruit}")
            assert get_fruit == "orange"
    

    This way, the fixture is called only once per fruit, per class.

    If you run your tests, you will have a total of 4 tests (2 tests x 2 parameters). However, there are only 2 network calls, one for each fruit.