pythonpytest

Overriding sub-fixtures in pytest


I'm using pytest with some complicated dependency-injected fixtures. I have fixtures that use other fixtures in a long chain. I'd like to be able to alter some fixtures in the middle of the chain for specific tests.

Given these (simplified) fixtures:

@pytest.fixture
def cache():
    return Cache()

# Use cache fixture in a new fixture.
@pytest.fixture
def resource(cache):
    return Resource(cache=cache, working=True)

# Use resource fixture in a new fixture.
@pytest.fixture
def service(resource):
    return Service(resource=resource)

And some tests:

def test_service_when_resource_working(service):
    assert service.status == "good"

def test_service_when_resource_broken(service):
    assert service.status == "bad"

How can I override the resource fixture so that it's like this:

@pytest.fixture
def broken_resource(cache):
    return Resource(cache=cache, working=False)

...but only for the test_service_when_resource_broken test case? I can create a broken_service that uses broken_resource, but the reality is that the dependency chain is long, and I want to re-use all the fixtures, but selectively change some of them in the middle for selected tests.

I want to do something like this (pseudocode):

@pytest.override_fixture('resource', 'broken_resource')
def test_service_when_resource_broken(service):
    # service should have been instantiated with broken_resource instead of resource.
    assert service.status == "bad"

Solution

  • You can use markers on your tests to achieve what you are expecting. Basically, you mark the test for which you need a different behaviour. In the fixture method look for that marker from the requesting test context and process.

    Here is how you can do it.

    @pytest.fixture
    def cache():
        return Cache()
    
    # Use cache fixture in a new fixture.
    
    
    @pytest.fixture
    def resource(request, cache):
        working = True
        marker = request.node.get_marker("broken")
        if marker:
            working = False
    
        return Resource(cache=cache, working=working)
    
    
    # Use resource fixture in a new fixture.
    @pytest.fixture
    def service(resource):
        return Service(resource=resource)
    
    
    def test_service_when_resource_working(service):
        assert service.status == "good"
    
    
    @pytest.mark.broken
    def test_service_when_resource_broken(service):
        assert service.status == "bad"