pytest: combine class level parametrization with class scoped fixtures

186 Views Asked by At

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.

1

There are 1 best solutions below

1
Hai Vu On BEST ANSWER

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.