DEV Community

Cover image for Python: pytest accessing Flask session and request context variables.
Be Hai Nguyen
Be Hai Nguyen

Posted on

Python: pytest accessing Flask session and request context variables.

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

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.
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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

with app.test_request_context():
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)