Search code examples
python-3.xpytestfixtures

Pytest change fixture scope at run time


I have common fixtures defined in conftest.py that is shared across all modules and is scoped to 'function' scope like the following:

conftest.py

@pytest.fixture(scope="function")
def reset_state(services):
    # resets the state of the services

And my test currently calls it like the following,

test_module_one:

# change scope to session here
@pytest.mark.usefixtures("reset_state")
def test_something:
    # Test stuff using session-scoped fixtures.

For the above specific test, I want to change the scope of the reset_state common fixture to 'session' instead.

Is there a way to change the scope at run time?


Solution

  • Since pytest version 5.2, there is support for dynamic scope. You can provide a custom callable as a fixture scope allowing it to determine the scope in the runtime.

    Example from documentation:

    def determine_scope(fixture_name, config):
        if config.getoption("--keep-containers", None):
            return "session"
        return "function"
    
    
    @pytest.fixture(scope=determine_scope)
    def docker_container():
        yield spawn_container()
    

    Please note that it does not allow you to change the runtime scope from test to test. If this is the case when you wish to use session scope for all tests except a few that changes the internal state of the fixture (so you would like to run them with function scope) there is a simple and very explicit workaround with referential transparency:

    Make one method to create the subject for the test and two fixtures of different scopes returning a result of such method.

    Example:

    import pytest
    
    
    def _make_fixture() -> object:
        """
        Returns:
            object: Some fixture for the test
        """
        return object()
    
    
    @pytest.fixture(scope="function")
    def function_scope_fixture() -> object:
        """
        Returns:
            object: Function scope use fixture
        """
        return _make_fixture()
    
    
    @pytest.fixture(scope="session")
    def session_scope_fixture() -> object:
        """
        Returns:
            object: Session scope use fixture
        """
        return _make_fixture()
    
    
    def test_1(session_scope_fixture):
        """Test without mutation of the fixture"""
        ...
    
    
    def test_2(function_scope_fixture):
        """Test mutating the internal state of the fixture"""
        ...