2025, Oct 23 17:00

How to Extend pytest Fixtures Across Class Inheritance Without Recursion: A Simple Aliasing Pattern

Learn how to extend pytest fixtures in class inheritance without recursive dependencies. Use an aliasing technique to reuse base fixtures and keep tests lean.

Extending Pytest fixtures is a common need: you don’t always want to override a fixture, sometimes you want to reuse the original and append your own data. That works fine in a simple case, but breaks in class-based inheritance where a fixture in a subclass attempts to call the fixture with the same name from the base class. The result is a recursive dependency error. Below is a practical walk-through of the problem and a minimal, reliable workaround.

Reusing a fixture inside the same class

A straightforward example of partially reusing a fixture within a class scope looks like this. A fixture returns a base list, and another fixture with the same name consumes it and concatenates more values. The test then sees the combined data:

import pytest
@pytest.fixture
def numbers():
    return [1, 2, 3]
class Suite:
    @pytest.fixture
    def numbers(self, numbers):
        return numbers + [4, 5, 6]
    def test_numbers(self, numbers):
        assert 1 in numbers
        assert 2 in numbers
        assert 3 in numbers
        assert 4 in numbers
        assert 5 in numbers
        assert 6 in numbers

This pattern works because the inner fixture depends on the outer one defined at module level; there is no ambiguity when resolving the name.

Where it breaks: class inheritance and recursive dependency

Now consider extending a class-level fixture by calling its base variant in an inherited class. The intent is the same, but the resolution differs and you hit a recursion trap:

import pytest
class ParentSuite:
    @pytest.fixture
    def numbers(self):
        return [1, 2, 3]
class ChildSuite(ParentSuite):
    @pytest.fixture
    def numbers(self, numbers):
        return numbers + [4, 5, 6]
    def test_numbers(self, numbers):
        assert 1 in numbers
        assert 2 in numbers
        assert 3 in numbers
        assert 4 in numbers
        assert 5 in numbers
        assert 6 in numbers

Here Pytest reports:

E recursive dependency involving fixture 'values' detected

The second fixture definition does not “see” the parent one under the same name. There is no function overloading in Python, and within the subclass the name resolves to the most local fixture, which ends up requiring itself, causing the recursion.

Why simply renaming the base fixture is not a solution

Renaming the base fixture might seem like a way out, but it breaks scenarios where shared tests target a specific fixture name across a hierarchy. Consider a reusable test defined on a base class, plus two subclasses that extend the same data set differently and also parametrize a separate fixture:

import pytest
class ParentSuite:
    @pytest.fixture
    def numbers(self):
        return [1, 2, 3]
    def test_contains(numbers, item):
        assert item in numbers
class SuiteAlpha(ParentSuite):
    @pytest.fixture
    def numbers(self, numbers):
        return numbers + [4, 5, 6]
    @pytest.fixture(params=[1, 2, 3, 4, 5, 6])
    def item(self):
        return request.param
class SuiteBeta(ParentSuite):
    @pytest.fixture
    def numbers(self, numbers):
        return numbers + [7, 8, 9]
    @pytest.fixture(params=[1, 2, 3, 7, 8, 9])
    def item(self):
        return request.param

The shared test relies on the fixture name numbers. If you rename the base fixture, you either have to touch all shared tests or insert indirection everywhere, which defeats the purpose of clean reuse.

Working fix: alias the base method and depend on the alias

The reliable approach is to create a method alias to the base class fixture and depend on that alias in the subclass. This keeps the public fixture name unchanged while allowing the subclass to compose from the base:

import pytest
class ParentSuite:
    @pytest.fixture
    def numbers(self):
        return [1, 2, 3]
class ChildSuite(ParentSuite):
    __seed_numbers = ParentSuite.numbers
    @pytest.fixture
    def numbers(self, __seed_numbers):
        return __seed_numbers + [4, 5, 6]
    def test_numbers(self, numbers):
        assert 1 in numbers
        assert 2 in numbers
        assert 3 in numbers
        assert 4 in numbers
        assert 5 in numbers
        assert 6 in numbers

This alias respects the decorators, and Pytest constructs a dependency from the subclass fixture to the base class fixture. The subclass continues to expose numbers to tests, but internally it builds on the aliased base implementation.

Why this matters

Test suites often rely on inheritance to share behavior, and fixtures are part of that contract. When a reusable test references a fixture name, subclasses should be able to refine that fixture without rewriting tests or resorting to multiple names for the same concept. Avoiding recursive dependency is crucial to keep test composition predictable and to prevent brittle workarounds that spread across the codebase.

Takeaways

If you need to extend, not replace, a class-level fixture under the same name, create a method alias to the base fixture and make the derived fixture depend on that alias. This sidesteps recursive dependency, preserves the fixture’s public name for shared tests, and keeps your test hierarchy consistent and maintainable.

The article is based on a question from StackOverflow by Dmitry Kuzminov and an answer by Dmitry Kuzminov.