Search code examples
pythonclasspytestfixtures

How to extend a pytest base class and override a fixture


I have a base test class that implements some tests that i want to re-use. Idea is to inherit from this class and override a fixture to provide necessary input data to the base class tests. Something like this:

base_class.py
class TestBase:
    @pytest.fixture(scope="module")
    def base_settings(self) -> dict:
        return None
    
    def test_settings(base_settings):
        assert base_settings["value_a"] > base_settings["value_b"]
test_my_class.py
class TestMyClass(TestBase):
    # override the base_settings fixture in TestBase
    @pytest.fixture(scope="module")
    def base_settings(self) -> dict:
        return {"value_a": 4, "value_b": 3}
}

When i now execute test_my_class.py I would expect the test_settings test to pass (value_a is greater than value_b) but instead it complains that base_settings is None -> fixture from base class is used instead of the one in TestMyClass. How can i fix this? I also tried passing parameters via __init__ method but it seems like this is not supported in pytest.


Solution

  • I've just tested in pytest 8.0.1 and it works the way you expect. What might be happening is that pytest executes your TestBase by itself, simply because you named it Test... and it therefore gets collected as any other test class.

    In the following example 2 tests are run, with one failing as expected not because it gets None but because I set both value_a and value_b to 3 just to prove that the fixture was indeed overridden between individual test classes:

    import pytest
    
    
    class Base:
        @pytest.fixture(scope="class")
        def base_settings(self) -> dict:
            return None
    
        def test_settings(self, base_settings):
            assert base_settings["value_a"] > base_settings["value_b"]
    
    
    class TestMyClassA(Base):
        @pytest.fixture(scope="class")
        def base_settings(self) -> dict:
            return {"value_a": 4, "value_b": 3}
    
    
    class TestMyClassB(Base):
        @pytest.fixture(scope="class")
        def base_settings(self) -> dict:
            return {"value_a": 3, "value_b": 3}
    

    Note: I changed scope to class because it seems more logical but the original module should work as well.