Pytest fixture with optional parameter change inside test case

Here’s a way of setting up a pytest fixture that sets up a default value automatically for any test case that uses the fixture, but allows calling the fixture from inside the test case to update that value where necessary.

The example is for mocking out API responses with httpx_mock, but this fixture arrangement can be used for any situation where you want a default fixture value that can optionally be modified from within a single test case.

from typing import Protocol


class GivenFoobarAPIResponse(Protocol):
	def __call__(self, new_foobar_response_body: dict[str, str]):
		...


@fixture
def given_foobar_api_response(
    httpx_mock: HTTPXMock,
) -> GivenFoobarAPIResponse:
    foobar_response_body = {"foobar": "default_foobar_value"}
    
    httpx_mock.add_response(
        url="foobar.test/something/123",
        method="GET",
        json=foobar_response_body,
    )

    def __given_foobar_api_response(
        new_foobar_response_body: dict[str, str],
    ) -> None:
        foobar_response_body.clear()
        foobar_response_body.update(new_foobar_response_body)

    yield __given_foobar_api_response

This fixture arrangement allows you to do this in pytest test cases:

def test__with_default_fixture_value(
	given_foobar_api_response: GivenFoobarAPIResponse,
):
	...
	# The mocked API will respond with the default response body without any
	# further setup being required in the test case.

def test__with_default_fixture_value(
	given_foobar_api_response: GivenFoobarAPIResponse,
):
	given_foobar_api_response(
		{"something_new": "this has been updated for this test case"},
	)
	...
	# The mocked API will respond with the specified response body for this test
	# case.

Tech mentioned