How to enable pytest methods to access Flask’s context variables, session and request.

036-feature-image.png
Python: pytest accessing Flask session and request context variables.

I’ve previously blogged about pytest’s application fixture and test client fixture in Python: pytest and Flask template context processor functions. ( Please see section pytest entry module conftest.py. )

The test client fixture in the mentioned post:

@pytest.fixture(scope='module')
def test_client( app ):
    """
    Creates a test client.
	app.test_client() is able to submit HTTP requests.
	
    The app argument is the app() fixture above.	
    """
	
    with app.test_client() as testing_client:
        yield testing_client  # Return to caller.

When testing the codes that access Flask’s context variable session, the above fixture will not work. To access this variable, the official document states:

If you want to access or set a value in the session before making a request, use the client’s session_transaction() method in a with statement. It returns a session object, and will save the session once the block ends.

https://flask.palletsprojects.com/en/2.2.x/testing/

I tried that, and it does not work… It’s a “popular” problem. It’s been around for a few years. There are a few posts on it, however, most suggestions fail to work for me.

Sessions are empty when testing #69 raises this problem, and user russmac suggests a solution:

@pytest.fixture(scope='module')
def test_client( app ):
    """
    Creates a test client.
	app.test_client() is able to submit HTTP requests.
	
    The app argument is the app() fixture above.	
    """
	
    with app.test_client() as testing_client:
        """
        See: https://github.com/pytest-dev/pytest-flask/issues/69 
		Sessions are empty when testing #69 
        """
        with testing_client.session_transaction() as session:
            session['Authorization'] = 'redacted'
			
        yield testing_client  # Return to caller.

This works for me. However, to access variable session my test codes must be within:

with app.test_request_context():

Or else, it raises the error RuntimeError: Working outside of request context. Also, without the above call, the codes proper would not be able to access the request variable during testing.

Following is a proper test method of a project I’m working on:

@pytest.mark.timesheet_bro
def test_close( app ):
    bro_obj = TimesheetBRO( 1 )

    #
    # RuntimeError: Working outside of request context.
    #
    # session[ 'user_id' ] = 100
    #

    with app.test_request_context( '?searchType={}'.format(UNRESTRICT_SEARCH_TYPE) ):
        assert request.args[ 'searchType' ] == UNRESTRICT_SEARCH_TYPE
        assert request.values.get( 'searchType' ) == UNRESTRICT_SEARCH_TYPE

        session[ 'user_id' ] = 1

        data = bro_obj.close( 326 )

    assert bro_obj.last_message == ''
    assert data['status']['code'] == HTTPStatus.OK.value

Please note, within the codes proper, session[ ‘user_id’ ] is set after a user has successfully logged in. Setting this value in the test codes to create a correct pre-condition for the codes being tested. Please also note, request.values.get( ‘searchType’ ) is also used in the codes under testing.

Following is another proper test method which does not submit any request parameters:

@pytest.mark.timesheet_bro
def test_update_last_timesheet_id( app ):
    bro_obj = TimesheetBRO( 1 )

    with app.test_request_context():
        session[ 'user_id' ] = 1

        data = bro_obj.update_last_timesheet_id( 1, 1123 )

    assert bro_obj.last_message == ''
    assert data['status']['code'] == HTTPStatus.OK.value

I hope you will find this helpful at some point… And thank you for reading.